diff options
Diffstat (limited to 'js')
133 files changed, 0 insertions, 21130 deletions
diff --git a/js/Cargo.toml b/js/Cargo.toml deleted file mode 100644 index 40cc43d93..000000000 --- a/js/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "deno_cli_snapshots" -version = "0.19.0" -license = "MIT" -authors = ["the Deno authors"] -edition = "2018" -description = "Provides snapshots for the deno CLI" -repository = "https://github.com/denoland/deno" -exclude = [ - "std/fs/testdata/0-link.ts", - "std/fs/testdata/copy_dir_link_file/0.txt", -] - -[lib] -path = "lib.rs" - -[dependencies] -deno_typescript = { path = "../deno_typescript", version = "0.19.0" } - -[dev-dependencies] -deno = { path = "../core", version = "0.19.0" } - -[build-dependencies] -deno_typescript = { path = "../deno_typescript", version = "0.19.0" } - diff --git a/js/README.md b/js/README.md deleted file mode 100644 index 850adb6ee..000000000 --- a/js/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Crate: `deno_cli_snapshots` - -## AKA `cli_snapshots` AKA `//js` - -This is a small crate which exports just a few static blobs. It contains a -build.rs file which compiles Deno's internal JavaScript and TypeScript code -first into a single AMD bundle, and then into a binary V8 Snapshot. - -The main Deno executable crate ("cli") depends on this crate and has access to -all the runtime code. diff --git a/js/base64.ts b/js/base64.ts deleted file mode 100644 index 4d30e00f1..000000000 --- a/js/base64.ts +++ /dev/null @@ -1,150 +0,0 @@ -// Forked from https://github.com/beatgammit/base64-js -// Copyright (c) 2014 Jameson Little. MIT License. - -const lookup: string[] = []; -const revLookup: number[] = []; - -const code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -for (let i = 0, len = code.length; i < len; ++i) { - lookup[i] = code[i]; - revLookup[code.charCodeAt(i)] = i; -} - -// Support decoding URL-safe base64 strings, as Node.js does. -// See: https://en.wikipedia.org/wiki/Base64#URL_applications -revLookup["-".charCodeAt(0)] = 62; -revLookup["_".charCodeAt(0)] = 63; - -function getLens(b64: string): [number, number] { - const len = b64.length; - - if (len % 4 > 0) { - throw new Error("Invalid string. Length must be a multiple of 4"); - } - - // Trim off extra bytes after placeholder bytes are found - // See: https://github.com/beatgammit/base64-js/issues/42 - let validLen = b64.indexOf("="); - if (validLen === -1) validLen = len; - - const placeHoldersLen = validLen === len ? 0 : 4 - (validLen % 4); - - return [validLen, placeHoldersLen]; -} - -// base64 is 4/3 + up to two characters of the original data -export function byteLength(b64: string): number { - const lens = getLens(b64); - const validLen = lens[0]; - const placeHoldersLen = lens[1]; - return ((validLen + placeHoldersLen) * 3) / 4 - placeHoldersLen; -} - -function _byteLength( - b64: string, - validLen: number, - placeHoldersLen: number -): number { - return ((validLen + placeHoldersLen) * 3) / 4 - placeHoldersLen; -} - -export function toByteArray(b64: string): Uint8Array { - let tmp; - const lens = getLens(b64); - const validLen = lens[0]; - const placeHoldersLen = lens[1]; - - const arr = new Uint8Array(_byteLength(b64, validLen, placeHoldersLen)); - - let curByte = 0; - - // if there are placeholders, only get up to the last complete 4 chars - const len = placeHoldersLen > 0 ? validLen - 4 : validLen; - - let i; - for (i = 0; i < len; i += 4) { - tmp = - (revLookup[b64.charCodeAt(i)] << 18) | - (revLookup[b64.charCodeAt(i + 1)] << 12) | - (revLookup[b64.charCodeAt(i + 2)] << 6) | - revLookup[b64.charCodeAt(i + 3)]; - arr[curByte++] = (tmp >> 16) & 0xff; - arr[curByte++] = (tmp >> 8) & 0xff; - arr[curByte++] = tmp & 0xff; - } - - if (placeHoldersLen === 2) { - tmp = - (revLookup[b64.charCodeAt(i)] << 2) | - (revLookup[b64.charCodeAt(i + 1)] >> 4); - arr[curByte++] = tmp & 0xff; - } - - if (placeHoldersLen === 1) { - tmp = - (revLookup[b64.charCodeAt(i)] << 10) | - (revLookup[b64.charCodeAt(i + 1)] << 4) | - (revLookup[b64.charCodeAt(i + 2)] >> 2); - arr[curByte++] = (tmp >> 8) & 0xff; - arr[curByte++] = tmp & 0xff; - } - - return arr; -} - -function tripletToBase64(num: number): string { - return ( - lookup[(num >> 18) & 0x3f] + - lookup[(num >> 12) & 0x3f] + - lookup[(num >> 6) & 0x3f] + - lookup[num & 0x3f] - ); -} - -function encodeChunk(uint8: Uint8Array, start: number, end: number): string { - let tmp; - const output = []; - for (let i = start; i < end; i += 3) { - tmp = - ((uint8[i] << 16) & 0xff0000) + - ((uint8[i + 1] << 8) & 0xff00) + - (uint8[i + 2] & 0xff); - output.push(tripletToBase64(tmp)); - } - return output.join(""); -} - -export function fromByteArray(uint8: Uint8Array): string { - let tmp; - const len = uint8.length; - const extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes - const parts = []; - const maxChunkLength = 16383; // must be multiple of 3 - - // go through the array every three bytes, we'll deal with trailing stuff later - for (let i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { - parts.push( - encodeChunk( - uint8, - i, - i + maxChunkLength > len2 ? len2 : i + maxChunkLength - ) - ); - } - - // pad the end with zeros, but make sure to not forget the extra bytes - if (extraBytes === 1) { - tmp = uint8[len - 1]; - parts.push(lookup[tmp >> 2] + lookup[(tmp << 4) & 0x3f] + "=="); - } else if (extraBytes === 2) { - tmp = (uint8[len - 2] << 8) + uint8[len - 1]; - parts.push( - lookup[tmp >> 10] + - lookup[(tmp >> 4) & 0x3f] + - lookup[(tmp << 2) & 0x3f] + - "=" - ); - } - - return parts.join(""); -} diff --git a/js/blob.ts b/js/blob.ts deleted file mode 100644 index 50ab7f374..000000000 --- a/js/blob.ts +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as domTypes from "./dom_types.ts"; -import { containsOnlyASCII, hasOwnProperty } from "./util.ts"; -import { TextEncoder } from "./text_encoding.ts"; -import { build } from "./build.ts"; - -export const bytesSymbol = Symbol("bytes"); - -function convertLineEndingsToNative(s: string): string { - const nativeLineEnd = build.os == "win" ? "\r\n" : "\n"; - - let position = 0; - - let collectionResult = collectSequenceNotCRLF(s, position); - - let token = collectionResult.collected; - position = collectionResult.newPosition; - - let result = token; - - while (position < s.length) { - const c = s.charAt(position); - if (c == "\r") { - result += nativeLineEnd; - position++; - if (position < s.length && s.charAt(position) == "\n") { - position++; - } - } else if (c == "\n") { - position++; - result += nativeLineEnd; - } - - collectionResult = collectSequenceNotCRLF(s, position); - - token = collectionResult.collected; - position = collectionResult.newPosition; - - result += token; - } - - return result; -} - -function collectSequenceNotCRLF( - s: string, - position: number -): { collected: string; newPosition: number } { - const start = position; - for ( - let c = s.charAt(position); - position < s.length && !(c == "\r" || c == "\n"); - c = s.charAt(++position) - ); - return { collected: s.slice(start, position), newPosition: position }; -} - -function toUint8Arrays( - blobParts: domTypes.BlobPart[], - doNormalizeLineEndingsToNative: boolean -): Uint8Array[] { - const ret: Uint8Array[] = []; - const enc = new TextEncoder(); - for (const element of blobParts) { - if (typeof element === "string") { - let str = element; - if (doNormalizeLineEndingsToNative) { - str = convertLineEndingsToNative(element); - } - ret.push(enc.encode(str)); - // eslint-disable-next-line @typescript-eslint/no-use-before-define - } else if (element instanceof DenoBlob) { - ret.push(element[bytesSymbol]); - } else if (element instanceof Uint8Array) { - ret.push(element); - } else if (element instanceof Uint16Array) { - const uint8 = new Uint8Array(element.buffer); - ret.push(uint8); - } else if (element instanceof Uint32Array) { - const uint8 = new Uint8Array(element.buffer); - ret.push(uint8); - } else if (ArrayBuffer.isView(element)) { - // Convert view to Uint8Array. - const uint8 = new Uint8Array(element.buffer); - ret.push(uint8); - } else if (element instanceof ArrayBuffer) { - // Create a new Uint8Array view for the given ArrayBuffer. - const uint8 = new Uint8Array(element); - ret.push(uint8); - } else { - ret.push(enc.encode(String(element))); - } - } - return ret; -} - -function processBlobParts( - blobParts: domTypes.BlobPart[], - options: domTypes.BlobPropertyBag -): Uint8Array { - const normalizeLineEndingsToNative = options.ending === "native"; - // ArrayBuffer.transfer is not yet implemented in V8, so we just have to - // pre compute size of the array buffer and do some sort of static allocation - // instead of dynamic allocation. - const uint8Arrays = toUint8Arrays(blobParts, normalizeLineEndingsToNative); - const byteLength = uint8Arrays - .map((u8): number => u8.byteLength) - .reduce((a, b): number => a + b, 0); - const ab = new ArrayBuffer(byteLength); - const bytes = new Uint8Array(ab); - - let courser = 0; - for (const u8 of uint8Arrays) { - bytes.set(u8, courser); - courser += u8.byteLength; - } - - return bytes; -} - -// A WeakMap holding blob to byte array mapping. -// Ensures it does not impact garbage collection. -export const blobBytesWeakMap = new WeakMap<domTypes.Blob, Uint8Array>(); - -export class DenoBlob implements domTypes.Blob { - private readonly [bytesSymbol]: Uint8Array; - readonly size: number = 0; - readonly type: string = ""; - - /** A blob object represents a file-like object of immutable, raw data. */ - constructor( - blobParts?: domTypes.BlobPart[], - options?: domTypes.BlobPropertyBag - ) { - if (arguments.length === 0) { - this[bytesSymbol] = new Uint8Array(); - return; - } - - options = options || {}; - // Set ending property's default value to "transparent". - if (!hasOwnProperty(options, "ending")) { - options.ending = "transparent"; - } - - if (options.type && !containsOnlyASCII(options.type)) { - const errMsg = "The 'type' property must consist of ASCII characters."; - throw new SyntaxError(errMsg); - } - - const bytes = processBlobParts(blobParts!, options); - // Normalize options.type. - let type = options.type ? options.type : ""; - if (type.length) { - for (let i = 0; i < type.length; ++i) { - const char = type[i]; - if (char < "\u0020" || char > "\u007E") { - type = ""; - break; - } - } - type = type.toLowerCase(); - } - // Set Blob object's properties. - this[bytesSymbol] = bytes; - this.size = bytes.byteLength; - this.type = type; - - // Register bytes for internal private use. - blobBytesWeakMap.set(this, bytes); - } - - slice(start?: number, end?: number, contentType?: string): DenoBlob { - return new DenoBlob([this[bytesSymbol].slice(start, end)], { - type: contentType || this.type - }); - } -} diff --git a/js/blob_test.ts b/js/blob_test.ts deleted file mode 100644 index afa1182a9..000000000 --- a/js/blob_test.ts +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, assert, assertEquals } from "./test_util.ts"; - -test(function blobString(): void { - const b1 = new Blob(["Hello World"]); - const str = "Test"; - const b2 = new Blob([b1, str]); - assertEquals(b2.size, b1.size + str.length); -}); - -test(function blobBuffer(): void { - const buffer = new ArrayBuffer(12); - const u8 = new Uint8Array(buffer); - const f1 = new Float32Array(buffer); - const b1 = new Blob([buffer, u8]); - assertEquals(b1.size, 2 * u8.length); - const b2 = new Blob([b1, f1]); - assertEquals(b2.size, 3 * u8.length); -}); - -test(function blobSlice(): void { - const blob = new Blob(["Deno", "Foo"]); - const b1 = blob.slice(0, 3, "Text/HTML"); - assert(b1 instanceof Blob); - assertEquals(b1.size, 3); - assertEquals(b1.type, "text/html"); - const b2 = blob.slice(-1, 3); - assertEquals(b2.size, 0); - const b3 = blob.slice(100, 3); - assertEquals(b3.size, 0); - const b4 = blob.slice(0, 10); - assertEquals(b4.size, blob.size); -}); - -test(function blobShouldNotThrowError(): void { - let hasThrown = false; - - try { - const options1: object = { - ending: "utf8", - hasOwnProperty: "hasOwnProperty" - }; - const options2: object = Object.create(null); - new Blob(["Hello World"], options1); - new Blob(["Hello World"], options2); - } catch { - hasThrown = true; - } - - assertEquals(hasThrown, false); -}); - -test(function nativeEndLine(): void { - const options: object = { - ending: "native" - }; - const blob = new Blob(["Hello\nWorld"], options); - - assertEquals(blob.size, Deno.build.os === "win" ? 12 : 11); -}); - -// TODO(qti3e) Test the stored data in a Blob after implementing FileReader API. diff --git a/js/body.ts b/js/body.ts deleted file mode 100644 index 6567b1934..000000000 --- a/js/body.ts +++ /dev/null @@ -1,272 +0,0 @@ -import * as formData from "./form_data.ts"; -import * as blob from "./blob.ts"; -import * as encoding from "./text_encoding.ts"; -import * as headers from "./headers.ts"; -import * as domTypes from "./dom_types.ts"; - -const { Headers } = headers; - -// only namespace imports work for now, plucking out what we need -const { FormData } = formData; -const { TextEncoder, TextDecoder } = encoding; -const Blob = blob.DenoBlob; -const DenoBlob = blob.DenoBlob; - -export type BodySource = - | domTypes.Blob - | domTypes.BufferSource - | domTypes.FormData - | domTypes.URLSearchParams - | domTypes.ReadableStream - | string; - -function validateBodyType(owner: Body, bodySource: BodySource): boolean { - if ( - bodySource instanceof Int8Array || - bodySource instanceof Int16Array || - bodySource instanceof Int32Array || - bodySource instanceof Uint8Array || - bodySource instanceof Uint16Array || - bodySource instanceof Uint32Array || - bodySource instanceof Uint8ClampedArray || - bodySource instanceof Float32Array || - bodySource instanceof Float64Array - ) { - return true; - } else if (bodySource instanceof ArrayBuffer) { - return true; - } else if (typeof bodySource === "string") { - return true; - } else if (bodySource instanceof FormData) { - return true; - } else if (!bodySource) { - return true; // null body is fine - } - throw new Error( - `Bad ${owner.constructor.name} body type: ${bodySource.constructor.name}` - ); -} - -function getHeaderValueParams(value: string): Map<string, string> { - const params = new Map(); - // Forced to do so for some Map constructor param mismatch - value - .split(";") - .slice(1) - .map((s): string[] => s.trim().split("=")) - .filter((arr): boolean => arr.length > 1) - .map(([k, v]): [string, string] => [k, v.replace(/^"([^"]*)"$/, "$1")]) - .forEach(([k, v]): Map<string, string> => params.set(k, v)); - return params; -} - -function hasHeaderValueOf(s: string, value: string): boolean { - return new RegExp(`^${value}[\t\s]*;?`).test(s); -} - -export const BodyUsedError = - "Failed to execute 'clone' on 'Body': body is already used"; - -export class Body implements domTypes.Body { - protected _stream: domTypes.ReadableStream | null; - - constructor(protected _bodySource: BodySource, readonly contentType: string) { - validateBodyType(this, _bodySource); - this._bodySource = _bodySource; - this.contentType = contentType; - this._stream = null; - } - - get body(): domTypes.ReadableStream | null { - if (this._stream) { - return this._stream; - } - if (typeof this._bodySource === "string") { - throw Error("not implemented"); - } - return this._stream; - } - - get bodyUsed(): boolean { - if (this.body && this.body.locked) { - return true; - } - return false; - } - - public async blob(): Promise<domTypes.Blob> { - return new Blob([await this.arrayBuffer()]); - } - - // ref: https://fetch.spec.whatwg.org/#body-mixin - public async formData(): Promise<domTypes.FormData> { - const formData = new FormData(); - const enc = new TextEncoder(); - if (hasHeaderValueOf(this.contentType, "multipart/form-data")) { - const params = getHeaderValueParams(this.contentType); - if (!params.has("boundary")) { - // TypeError is required by spec - throw new TypeError("multipart/form-data must provide a boundary"); - } - // ref: https://tools.ietf.org/html/rfc2046#section-5.1 - const boundary = params.get("boundary")!; - const dashBoundary = `--${boundary}`; - const delimiter = `\r\n${dashBoundary}`; - const closeDelimiter = `${delimiter}--`; - - const body = await this.text(); - let bodyParts: string[]; - const bodyEpilogueSplit = body.split(closeDelimiter); - if (bodyEpilogueSplit.length < 2) { - bodyParts = []; - } else { - // discard epilogue - const bodyEpilogueTrimmed = bodyEpilogueSplit[0]; - // first boundary treated special due to optional prefixed \r\n - const firstBoundaryIndex = bodyEpilogueTrimmed.indexOf(dashBoundary); - if (firstBoundaryIndex < 0) { - throw new TypeError("Invalid boundary"); - } - const bodyPreambleTrimmed = bodyEpilogueTrimmed - .slice(firstBoundaryIndex + dashBoundary.length) - .replace(/^[\s\r\n\t]+/, ""); // remove transport-padding CRLF - // trimStart might not be available - // Be careful! body-part allows trailing \r\n! - // (as long as it is not part of `delimiter`) - bodyParts = bodyPreambleTrimmed - .split(delimiter) - .map((s): string => s.replace(/^[\s\r\n\t]+/, "")); - // TODO: LWSP definition is actually trickier, - // but should be fine in our case since without headers - // we should just discard the part - } - for (const bodyPart of bodyParts) { - const headers = new Headers(); - const headerOctetSeperatorIndex = bodyPart.indexOf("\r\n\r\n"); - if (headerOctetSeperatorIndex < 0) { - continue; // Skip unknown part - } - const headerText = bodyPart.slice(0, headerOctetSeperatorIndex); - const octets = bodyPart.slice(headerOctetSeperatorIndex + 4); - - // TODO: use textproto.readMIMEHeader from deno_std - const rawHeaders = headerText.split("\r\n"); - for (const rawHeader of rawHeaders) { - const sepIndex = rawHeader.indexOf(":"); - if (sepIndex < 0) { - continue; // Skip this header - } - const key = rawHeader.slice(0, sepIndex); - const value = rawHeader.slice(sepIndex + 1); - headers.set(key, value); - } - if (!headers.has("content-disposition")) { - continue; // Skip unknown part - } - // Content-Transfer-Encoding Deprecated - const contentDisposition = headers.get("content-disposition")!; - const partContentType = headers.get("content-type") || "text/plain"; - // TODO: custom charset encoding (needs TextEncoder support) - // const contentTypeCharset = - // getHeaderValueParams(partContentType).get("charset") || ""; - if (!hasHeaderValueOf(contentDisposition, "form-data")) { - continue; // Skip, might not be form-data - } - const dispositionParams = getHeaderValueParams(contentDisposition); - if (!dispositionParams.has("name")) { - continue; // Skip, unknown name - } - const dispositionName = dispositionParams.get("name")!; - if (dispositionParams.has("filename")) { - const filename = dispositionParams.get("filename")!; - const blob = new DenoBlob([enc.encode(octets)], { - type: partContentType - }); - // TODO: based on spec - // https://xhr.spec.whatwg.org/#dom-formdata-append - // https://xhr.spec.whatwg.org/#create-an-entry - // Currently it does not mention how I could pass content-type - // to the internally created file object... - formData.append(dispositionName, blob, filename); - } else { - formData.append(dispositionName, octets); - } - } - return formData; - } else if ( - hasHeaderValueOf(this.contentType, "application/x-www-form-urlencoded") - ) { - // From https://github.com/github/fetch/blob/master/fetch.js - // Copyright (c) 2014-2016 GitHub, Inc. MIT License - const body = await this.text(); - try { - body - .trim() - .split("&") - .forEach( - (bytes): void => { - if (bytes) { - const split = bytes.split("="); - const name = split.shift()!.replace(/\+/g, " "); - const value = split.join("=").replace(/\+/g, " "); - formData.append( - decodeURIComponent(name), - decodeURIComponent(value) - ); - } - } - ); - } catch (e) { - throw new TypeError("Invalid form urlencoded format"); - } - return formData; - } else { - throw new TypeError("Invalid form data"); - } - } - - public async text(): Promise<string> { - if (typeof this._bodySource === "string") { - return this._bodySource; - } - - const ab = await this.arrayBuffer(); - const decoder = new TextDecoder("utf-8"); - return decoder.decode(ab); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public async json(): Promise<any> { - const raw = await this.text(); - return JSON.parse(raw); - } - - public async arrayBuffer(): Promise<ArrayBuffer> { - if ( - this._bodySource instanceof Int8Array || - this._bodySource instanceof Int16Array || - this._bodySource instanceof Int32Array || - this._bodySource instanceof Uint8Array || - this._bodySource instanceof Uint16Array || - this._bodySource instanceof Uint32Array || - this._bodySource instanceof Uint8ClampedArray || - this._bodySource instanceof Float32Array || - this._bodySource instanceof Float64Array - ) { - return this._bodySource.buffer as ArrayBuffer; - } else if (this._bodySource instanceof ArrayBuffer) { - return this._bodySource; - } else if (typeof this._bodySource === "string") { - const enc = new TextEncoder(); - return enc.encode(this._bodySource).buffer as ArrayBuffer; - } else if (this._bodySource instanceof FormData) { - const enc = new TextEncoder(); - return enc.encode(this._bodySource.toString()).buffer as ArrayBuffer; - } else if (!this._bodySource) { - return new ArrayBuffer(0); - } - throw new Error( - `Body type not yet implemented: ${this._bodySource.constructor.name}` - ); - } -} diff --git a/js/body_test.ts b/js/body_test.ts deleted file mode 100644 index ec76e9072..000000000 --- a/js/body_test.ts +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, testPerm, assertEquals, assert } from "./test_util.ts"; - -// just a hack to get a body object -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function buildBody(body: any): Body { - const stub = new Request("", { - body: body - }); - return stub as Body; -} - -const intArrays = [ - Int8Array, - Int16Array, - Int32Array, - Uint8Array, - Uint16Array, - Uint32Array, - Uint8ClampedArray, - Float32Array, - Float64Array -]; -test(async function arrayBufferFromByteArrays(): Promise<void> { - const buffer = new TextEncoder().encode("ahoyhoy8").buffer; - - for (const type of intArrays) { - const body = buildBody(new type(buffer)); - const text = new TextDecoder("utf-8").decode(await body.arrayBuffer()); - assertEquals(text, "ahoyhoy8"); - } -}); - -//FormData -testPerm({ net: true }, async function bodyMultipartFormData(): Promise<void> { - const response = await fetch( - "http://localhost:4545/tests/subdir/multipart_form_data.txt" - ); - const text = await response.text(); - - const body = buildBody(text); - - // @ts-ignore - body.contentType = "multipart/form-data;boundary=boundary"; - - const formData = await body.formData(); - assert(formData.has("field_1")); - assertEquals(formData.get("field_1").toString(), "value_1 \r\n"); - assert(formData.has("field_2")); -}); - -testPerm({ net: true }, async function bodyURLEncodedFormData(): Promise<void> { - const response = await fetch( - "http://localhost:4545/tests/subdir/form_urlencoded.txt" - ); - const text = await response.text(); - - const body = buildBody(text); - - // @ts-ignore - body.contentType = "application/x-www-form-urlencoded"; - - const formData = await body.formData(); - assert(formData.has("field_1")); - assertEquals(formData.get("field_1").toString(), "Hi"); - assert(formData.has("field_2")); - assertEquals(formData.get("field_2").toString(), "<Deno>"); -}); diff --git a/js/buffer.ts b/js/buffer.ts deleted file mode 100644 index dc73b7e60..000000000 --- a/js/buffer.ts +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. - -// This code has been ported almost directly from Go's src/bytes/buffer.go -// Copyright 2009 The Go Authors. All rights reserved. BSD license. -// https://github.com/golang/go/blob/master/LICENSE - -import { Reader, Writer, EOF, SyncReader, SyncWriter } from "./io.ts"; -import { assert } from "./util.ts"; -import { TextDecoder } from "./text_encoding.ts"; -import { DenoError, ErrorKind } from "./errors.ts"; - -// MIN_READ is the minimum ArrayBuffer size passed to a read call by -// buffer.ReadFrom. As long as the Buffer has at least MIN_READ bytes beyond -// what is required to hold the contents of r, readFrom() will not grow the -// underlying buffer. -const MIN_READ = 512; -const MAX_SIZE = 2 ** 32 - 2; - -// `off` is the offset into `dst` where it will at which to begin writing values -// from `src`. -// Returns the number of bytes copied. -function copyBytes(dst: Uint8Array, src: Uint8Array, off = 0): number { - const r = dst.byteLength - off; - if (src.byteLength > r) { - src = src.subarray(0, r); - } - dst.set(src, off); - return src.byteLength; -} - -/** A Buffer is a variable-sized buffer of bytes with read() and write() - * methods. Based on https://golang.org/pkg/bytes/#Buffer - */ -export class Buffer implements Reader, SyncReader, Writer, SyncWriter { - private buf: Uint8Array; // contents are the bytes buf[off : len(buf)] - private off = 0; // read at buf[off], write at buf[buf.byteLength] - - constructor(ab?: ArrayBuffer) { - if (ab == null) { - this.buf = new Uint8Array(0); - return; - } - - this.buf = new Uint8Array(ab); - } - - /** bytes() returns a slice holding the unread portion of the buffer. - * The slice is valid for use only until the next buffer modification (that - * is, only until the next call to a method like read(), write(), reset(), or - * truncate()). The slice aliases the buffer content at least until the next - * buffer modification, so immediate changes to the slice will affect the - * result of future reads. - */ - bytes(): Uint8Array { - return this.buf.subarray(this.off); - } - - /** toString() returns the contents of the unread portion of the buffer - * as a string. Warning - if multibyte characters are present when data is - * flowing through the buffer, this method may result in incorrect strings - * due to a character being split. - */ - toString(): string { - const decoder = new TextDecoder(); - return decoder.decode(this.buf.subarray(this.off)); - } - - /** empty() returns whether the unread portion of the buffer is empty. */ - empty(): boolean { - return this.buf.byteLength <= this.off; - } - - /** length is a getter that returns the number of bytes of the unread - * portion of the buffer - */ - get length(): number { - return this.buf.byteLength - this.off; - } - - /** Returns the capacity of the buffer's underlying byte slice, that is, - * the total space allocated for the buffer's data. - */ - get capacity(): number { - return this.buf.buffer.byteLength; - } - - /** truncate() discards all but the first n unread bytes from the buffer but - * continues to use the same allocated storage. It throws if n is negative or - * greater than the length of the buffer. - */ - truncate(n: number): void { - if (n === 0) { - this.reset(); - return; - } - if (n < 0 || n > this.length) { - throw Error("bytes.Buffer: truncation out of range"); - } - this._reslice(this.off + n); - } - - /** reset() resets the buffer to be empty, but it retains the underlying - * storage for use by future writes. reset() is the same as truncate(0) - */ - reset(): void { - this._reslice(0); - this.off = 0; - } - - /** _tryGrowByReslice() is a version of grow for the fast-case - * where the internal buffer only needs to be resliced. It returns the index - * where bytes should be written and whether it succeeded. - * It returns -1 if a reslice was not needed. - */ - private _tryGrowByReslice(n: number): number { - const l = this.buf.byteLength; - if (n <= this.capacity - l) { - this._reslice(l + n); - return l; - } - return -1; - } - - private _reslice(len: number): void { - assert(len <= this.buf.buffer.byteLength); - this.buf = new Uint8Array(this.buf.buffer, 0, len); - } - - /** readSync() reads the next len(p) bytes from the buffer or until the buffer - * is drained. The return value n is the number of bytes read. If the - * buffer has no data to return, eof in the response will be true. - */ - readSync(p: Uint8Array): number | EOF { - if (this.empty()) { - // Buffer is empty, reset to recover space. - this.reset(); - if (p.byteLength === 0) { - // this edge case is tested in 'bufferReadEmptyAtEOF' test - return 0; - } - return EOF; - } - const nread = copyBytes(p, this.buf.subarray(this.off)); - this.off += nread; - return nread; - } - - async read(p: Uint8Array): Promise<number | EOF> { - const rr = this.readSync(p); - return Promise.resolve(rr); - } - - writeSync(p: Uint8Array): number { - const m = this._grow(p.byteLength); - return copyBytes(this.buf, p, m); - } - - async write(p: Uint8Array): Promise<number> { - const n = this.writeSync(p); - return Promise.resolve(n); - } - - /** _grow() grows the buffer to guarantee space for n more bytes. - * It returns the index where bytes should be written. - * If the buffer can't grow it will throw with ErrTooLarge. - */ - private _grow(n: number): number { - const m = this.length; - // If buffer is empty, reset to recover space. - if (m === 0 && this.off !== 0) { - this.reset(); - } - // Fast: Try to grow by means of a reslice. - const i = this._tryGrowByReslice(n); - if (i >= 0) { - return i; - } - const c = this.capacity; - if (n <= Math.floor(c / 2) - m) { - // We can slide things down instead of allocating a new - // ArrayBuffer. We only need m+n <= c to slide, but - // we instead let capacity get twice as large so we - // don't spend all our time copying. - copyBytes(this.buf, this.buf.subarray(this.off)); - } else if (c > MAX_SIZE - c - n) { - throw new DenoError( - ErrorKind.TooLarge, - "The buffer cannot be grown beyond the maximum size." - ); - } else { - // Not enough space anywhere, we need to allocate. - const buf = new Uint8Array(2 * c + n); - copyBytes(buf, this.buf.subarray(this.off)); - this.buf = buf; - } - // Restore this.off and len(this.buf). - this.off = 0; - this._reslice(m + n); - return m; - } - - /** grow() grows the buffer's capacity, if necessary, to guarantee space for - * another n bytes. After grow(n), at least n bytes can be written to the - * buffer without another allocation. If n is negative, grow() will panic. If - * the buffer can't grow it will throw ErrTooLarge. - * Based on https://golang.org/pkg/bytes/#Buffer.Grow - */ - grow(n: number): void { - if (n < 0) { - throw Error("Buffer.grow: negative count"); - } - const m = this._grow(n); - this._reslice(m); - } - - /** readFrom() reads data from r until EOF and appends it to the buffer, - * growing the buffer as needed. It returns the number of bytes read. If the - * buffer becomes too large, readFrom will panic with ErrTooLarge. - * Based on https://golang.org/pkg/bytes/#Buffer.ReadFrom - */ - async readFrom(r: Reader): Promise<number> { - let n = 0; - while (true) { - try { - const i = this._grow(MIN_READ); - this._reslice(i); - const fub = new Uint8Array(this.buf.buffer, i); - const nread = await r.read(fub); - if (nread === EOF) { - return n; - } - this._reslice(i + nread); - n += nread; - } catch (e) { - return n; - } - } - } - - /** Sync version of `readFrom` - */ - readFromSync(r: SyncReader): number { - let n = 0; - while (true) { - try { - const i = this._grow(MIN_READ); - this._reslice(i); - const fub = new Uint8Array(this.buf.buffer, i); - const nread = r.readSync(fub); - if (nread === EOF) { - return n; - } - this._reslice(i + nread); - n += nread; - } catch (e) { - return n; - } - } - } -} - -/** Read `r` until EOF and return the content as `Uint8Array`. - */ -export async function readAll(r: Reader): Promise<Uint8Array> { - const buf = new Buffer(); - await buf.readFrom(r); - return buf.bytes(); -} - -/** Read synchronously `r` until EOF and return the content as `Uint8Array`. - */ -export function readAllSync(r: SyncReader): Uint8Array { - const buf = new Buffer(); - buf.readFromSync(r); - return buf.bytes(); -} - -/** Write all the content of `arr` to `w`. - */ -export async function writeAll(w: Writer, arr: Uint8Array): Promise<void> { - let nwritten = 0; - while (nwritten < arr.length) { - nwritten += await w.write(arr.subarray(nwritten)); - } -} - -/** Write synchronously all the content of `arr` to `w`. - */ -export function writeAllSync(w: SyncWriter, arr: Uint8Array): void { - let nwritten = 0; - while (nwritten < arr.length) { - nwritten += w.writeSync(arr.subarray(nwritten)); - } -} diff --git a/js/buffer_test.ts b/js/buffer_test.ts deleted file mode 100644 index a157b927e..000000000 --- a/js/buffer_test.ts +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. - -// This code has been ported almost directly from Go's src/bytes/buffer_test.go -// Copyright 2009 The Go Authors. All rights reserved. BSD license. -// https://github.com/golang/go/blob/master/LICENSE -import { assertEquals, test } from "./test_util.ts"; - -const { Buffer, readAll, readAllSync, writeAll, writeAllSync } = Deno; -type Buffer = Deno.Buffer; - -// N controls how many iterations of certain checks are performed. -const N = 100; -let testBytes: Uint8Array | null; -let testString: string | null; - -function init(): void { - if (testBytes == null) { - testBytes = new Uint8Array(N); - for (let i = 0; i < N; i++) { - testBytes[i] = "a".charCodeAt(0) + (i % 26); - } - const decoder = new TextDecoder(); - testString = decoder.decode(testBytes); - } -} - -function check(buf: Deno.Buffer, s: string): void { - const bytes = buf.bytes(); - assertEquals(buf.length, bytes.byteLength); - const decoder = new TextDecoder(); - const bytesStr = decoder.decode(bytes); - assertEquals(bytesStr, s); - assertEquals(buf.length, buf.toString().length); - assertEquals(buf.length, s.length); -} - -// Fill buf through n writes of byte slice fub. -// The initial contents of buf corresponds to the string s; -// the result is the final contents of buf returned as a string. -async function fillBytes( - buf: Buffer, - s: string, - n: number, - fub: Uint8Array -): Promise<string> { - check(buf, s); - for (; n > 0; n--) { - const m = await buf.write(fub); - assertEquals(m, fub.byteLength); - const decoder = new TextDecoder(); - s += decoder.decode(fub); - check(buf, s); - } - return s; -} - -// Empty buf through repeated reads into fub. -// The initial contents of buf corresponds to the string s. -async function empty(buf: Buffer, s: string, fub: Uint8Array): Promise<void> { - check(buf, s); - while (true) { - const r = await buf.read(fub); - if (r === Deno.EOF) { - break; - } - s = s.slice(r); - check(buf, s); - } - check(buf, ""); -} - -function repeat(c: string, bytes: number): Uint8Array { - assertEquals(c.length, 1); - const ui8 = new Uint8Array(bytes); - ui8.fill(c.charCodeAt(0)); - return ui8; -} - -test(function bufferNewBuffer(): void { - init(); - const buf = new Buffer(testBytes.buffer as ArrayBuffer); - check(buf, testString); -}); - -test(async function bufferBasicOperations(): Promise<void> { - init(); - const buf = new Buffer(); - for (let i = 0; i < 5; i++) { - check(buf, ""); - - buf.reset(); - check(buf, ""); - - buf.truncate(0); - check(buf, ""); - - let n = await buf.write(testBytes.subarray(0, 1)); - assertEquals(n, 1); - check(buf, "a"); - - n = await buf.write(testBytes.subarray(1, 2)); - assertEquals(n, 1); - check(buf, "ab"); - - n = await buf.write(testBytes.subarray(2, 26)); - assertEquals(n, 24); - check(buf, testString.slice(0, 26)); - - buf.truncate(26); - check(buf, testString.slice(0, 26)); - - buf.truncate(20); - check(buf, testString.slice(0, 20)); - - await empty(buf, testString.slice(0, 20), new Uint8Array(5)); - await empty(buf, "", new Uint8Array(100)); - - // TODO buf.writeByte() - // TODO buf.readByte() - } -}); - -test(async function bufferReadEmptyAtEOF(): Promise<void> { - // check that EOF of 'buf' is not reached (even though it's empty) if - // results are written to buffer that has 0 length (ie. it can't store any data) - const buf = new Buffer(); - const zeroLengthTmp = new Uint8Array(0); - const result = await buf.read(zeroLengthTmp); - assertEquals(result, 0); -}); - -test(async function bufferLargeByteWrites(): Promise<void> { - init(); - const buf = new Buffer(); - const limit = 9; - for (let i = 3; i < limit; i += 3) { - const s = await fillBytes(buf, "", 5, testBytes); - await empty(buf, s, new Uint8Array(Math.floor(testString.length / i))); - } - check(buf, ""); -}); - -test(async function bufferTooLargeByteWrites(): Promise<void> { - init(); - const tmp = new Uint8Array(72); - const growLen = Number.MAX_VALUE; - const xBytes = repeat("x", 0); - const buf = new Buffer(xBytes.buffer as ArrayBuffer); - await buf.read(tmp); - - let err; - try { - buf.grow(growLen); - } catch (e) { - err = e; - } - - assertEquals(err.kind, Deno.ErrorKind.TooLarge); - assertEquals(err.name, "TooLarge"); -}); - -test(async function bufferLargeByteReads(): Promise<void> { - init(); - const buf = new Buffer(); - for (let i = 3; i < 30; i += 3) { - const n = Math.floor(testBytes.byteLength / i); - const s = await fillBytes(buf, "", 5, testBytes.subarray(0, n)); - await empty(buf, s, new Uint8Array(testString.length)); - } - check(buf, ""); -}); - -test(function bufferCapWithPreallocatedSlice(): void { - const buf = new Buffer(new ArrayBuffer(10)); - assertEquals(buf.capacity, 10); -}); - -test(async function bufferReadFrom(): Promise<void> { - init(); - const buf = new Buffer(); - for (let i = 3; i < 30; i += 3) { - const s = await fillBytes( - buf, - "", - 5, - testBytes.subarray(0, Math.floor(testBytes.byteLength / i)) - ); - const b = new Buffer(); - await b.readFrom(buf); - const fub = new Uint8Array(testString.length); - await empty(b, s, fub); - } -}); - -test(async function bufferReadFromSync(): Promise<void> { - init(); - const buf = new Buffer(); - for (let i = 3; i < 30; i += 3) { - const s = await fillBytes( - buf, - "", - 5, - testBytes.subarray(0, Math.floor(testBytes.byteLength / i)) - ); - const b = new Buffer(); - b.readFromSync(buf); - const fub = new Uint8Array(testString.length); - await empty(b, s, fub); - } -}); - -test(async function bufferTestGrow(): Promise<void> { - const tmp = new Uint8Array(72); - for (const startLen of [0, 100, 1000, 10000, 100000]) { - const xBytes = repeat("x", startLen); - for (const growLen of [0, 100, 1000, 10000, 100000]) { - const buf = new Buffer(xBytes.buffer as ArrayBuffer); - // If we read, this affects buf.off, which is good to test. - const result = await buf.read(tmp); - const nread = result === Deno.EOF ? 0 : result; - buf.grow(growLen); - const yBytes = repeat("y", growLen); - await buf.write(yBytes); - // Check that buffer has correct data. - assertEquals( - buf.bytes().subarray(0, startLen - nread), - xBytes.subarray(nread) - ); - assertEquals( - buf.bytes().subarray(startLen - nread, startLen - nread + growLen), - yBytes - ); - } - } -}); - -test(async function testReadAll(): Promise<void> { - init(); - const reader = new Buffer(testBytes.buffer as ArrayBuffer); - const actualBytes = await readAll(reader); - assertEquals(testBytes.byteLength, actualBytes.byteLength); - for (let i = 0; i < testBytes.length; ++i) { - assertEquals(testBytes[i], actualBytes[i]); - } -}); - -test(function testReadAllSync(): void { - init(); - const reader = new Buffer(testBytes.buffer as ArrayBuffer); - const actualBytes = readAllSync(reader); - assertEquals(testBytes.byteLength, actualBytes.byteLength); - for (let i = 0; i < testBytes.length; ++i) { - assertEquals(testBytes[i], actualBytes[i]); - } -}); - -test(async function testWriteAll(): Promise<void> { - init(); - const writer = new Buffer(); - await writeAll(writer, testBytes); - const actualBytes = writer.bytes(); - assertEquals(testBytes.byteLength, actualBytes.byteLength); - for (let i = 0; i < testBytes.length; ++i) { - assertEquals(testBytes[i], actualBytes[i]); - } -}); - -test(function testWriteAllSync(): void { - init(); - const writer = new Buffer(); - writeAllSync(writer, testBytes); - const actualBytes = writer.bytes(); - assertEquals(testBytes.byteLength, actualBytes.byteLength); - for (let i = 0; i < testBytes.length; ++i) { - assertEquals(testBytes[i], actualBytes[i]); - } -}); diff --git a/js/build.rs b/js/build.rs deleted file mode 100644 index 1063fd8eb..000000000 --- a/js/build.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -use std::env; -use std::path::PathBuf; - -fn main() { - // To debug snapshot issues uncomment: - // deno_typescript::trace_serializer(); - - println!( - "cargo:rustc-env=TS_VERSION={}", - deno_typescript::ts_version() - ); - - let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); - let o = PathBuf::from(env::var_os("OUT_DIR").unwrap()); - - let root_names = vec![c.join("main.ts")]; - let bundle = o.join("CLI_SNAPSHOT.js"); - let state = deno_typescript::compile_bundle(&bundle, root_names).unwrap(); - assert!(bundle.exists()); - deno_typescript::mksnapshot_bundle(&bundle, state).unwrap(); - - let root_names = vec![c.join("compiler.ts")]; - let bundle = o.join("COMPILER_SNAPSHOT.js"); - let state = deno_typescript::compile_bundle(&bundle, root_names).unwrap(); - assert!(bundle.exists()); - deno_typescript::mksnapshot_bundle_ts(&bundle, state).unwrap(); -} diff --git a/js/build.ts b/js/build.ts deleted file mode 100644 index 942f57458..000000000 --- a/js/build.ts +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. - -export type OperatingSystem = "mac" | "win" | "linux"; - -export type Arch = "x64" | "arm64"; - -// Do not add unsupported platforms. -/** Build related information */ -export interface BuildInfo { - /** The CPU architecture. */ - arch: Arch; - - /** The operating system. */ - os: OperatingSystem; -} - -export const build: BuildInfo = { - arch: "" as Arch, - os: "" as OperatingSystem -}; - -export function setBuildInfo(os: OperatingSystem, arch: Arch): void { - build.os = os; - build.arch = arch; - - Object.freeze(build); -} diff --git a/js/build_test.ts b/js/build_test.ts deleted file mode 100644 index 4423de338..000000000 --- a/js/build_test.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, assert } from "./test_util.ts"; - -test(function buildInfo(): void { - // Deno.build is injected by rollup at compile time. Here - // we check it has been properly transformed. - const { arch, os } = Deno.build; - assert(arch === "x64"); - assert(os === "mac" || os === "win" || os === "linux"); -}); diff --git a/js/chmod.ts b/js/chmod.ts deleted file mode 100644 index 7bf54cc5b..000000000 --- a/js/chmod.ts +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendSync, sendAsync } from "./dispatch_json.ts"; -import * as dispatch from "./dispatch.ts"; - -/** Changes the permission of a specific file/directory of specified path - * synchronously. - * - * Deno.chmodSync("/path/to/file", 0o666); - */ -export function chmodSync(path: string, mode: number): void { - sendSync(dispatch.OP_CHMOD, { path, mode }); -} - -/** Changes the permission of a specific file/directory of specified path. - * - * await Deno.chmod("/path/to/file", 0o666); - */ -export async function chmod(path: string, mode: number): Promise<void> { - await sendAsync(dispatch.OP_CHMOD, { path, mode }); -} diff --git a/js/chmod_test.ts b/js/chmod_test.ts deleted file mode 100644 index 420f4f313..000000000 --- a/js/chmod_test.ts +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { testPerm, assertEquals } from "./test_util.ts"; - -const isNotWindows = Deno.build.os !== "win"; - -testPerm({ read: true, write: true }, function chmodSyncSuccess(): void { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const tempDir = Deno.makeTempDirSync(); - const filename = tempDir + "/test.txt"; - Deno.writeFileSync(filename, data, { perm: 0o666 }); - - // On windows no effect, but should not crash - Deno.chmodSync(filename, 0o777); - - // Check success when not on windows - if (isNotWindows) { - const fileInfo = Deno.statSync(filename); - assertEquals(fileInfo.mode & 0o777, 0o777); - } -}); - -// Check symlink when not on windows -if (isNotWindows) { - testPerm( - { read: true, write: true }, - function chmodSyncSymlinkSuccess(): void { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const tempDir = Deno.makeTempDirSync(); - - const filename = tempDir + "/test.txt"; - Deno.writeFileSync(filename, data, { perm: 0o666 }); - const symlinkName = tempDir + "/test_symlink.txt"; - Deno.symlinkSync(filename, symlinkName); - - let symlinkInfo = Deno.lstatSync(symlinkName); - const symlinkMode = symlinkInfo.mode & 0o777; // platform dependent - - Deno.chmodSync(symlinkName, 0o777); - - // Change actual file mode, not symlink - const fileInfo = Deno.statSync(filename); - assertEquals(fileInfo.mode & 0o777, 0o777); - symlinkInfo = Deno.lstatSync(symlinkName); - assertEquals(symlinkInfo.mode & 0o777, symlinkMode); - } - ); -} - -testPerm({ write: true }, function chmodSyncFailure(): void { - let err; - try { - const filename = "/badfile.txt"; - Deno.chmodSync(filename, 0o777); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); - -testPerm({ write: false }, function chmodSyncPerm(): void { - let err; - try { - Deno.chmodSync("/somefile.txt", 0o777); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -testPerm({ read: true, write: true }, async function chmodSuccess(): Promise< - void -> { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const tempDir = Deno.makeTempDirSync(); - const filename = tempDir + "/test.txt"; - Deno.writeFileSync(filename, data, { perm: 0o666 }); - - // On windows no effect, but should not crash - await Deno.chmod(filename, 0o777); - - // Check success when not on windows - if (isNotWindows) { - const fileInfo = Deno.statSync(filename); - assertEquals(fileInfo.mode & 0o777, 0o777); - } -}); - -// Check symlink when not on windows -if (isNotWindows) { - testPerm( - { read: true, write: true }, - async function chmodSymlinkSuccess(): Promise<void> { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const tempDir = Deno.makeTempDirSync(); - - const filename = tempDir + "/test.txt"; - Deno.writeFileSync(filename, data, { perm: 0o666 }); - const symlinkName = tempDir + "/test_symlink.txt"; - Deno.symlinkSync(filename, symlinkName); - - let symlinkInfo = Deno.lstatSync(symlinkName); - const symlinkMode = symlinkInfo.mode & 0o777; // platform dependent - - await Deno.chmod(symlinkName, 0o777); - - // Just change actual file mode, not symlink - const fileInfo = Deno.statSync(filename); - assertEquals(fileInfo.mode & 0o777, 0o777); - symlinkInfo = Deno.lstatSync(symlinkName); - assertEquals(symlinkInfo.mode & 0o777, symlinkMode); - } - ); -} - -testPerm({ write: true }, async function chmodFailure(): Promise<void> { - let err; - try { - const filename = "/badfile.txt"; - await Deno.chmod(filename, 0o777); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); - -testPerm({ write: false }, async function chmodPerm(): Promise<void> { - let err; - try { - await Deno.chmod("/somefile.txt", 0o777); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); diff --git a/js/chown.ts b/js/chown.ts deleted file mode 100644 index a8bad1193..000000000 --- a/js/chown.ts +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendSync, sendAsync } from "./dispatch_json.ts"; -import * as dispatch from "./dispatch.ts"; - -/** - * Change owner of a regular file or directory synchronously. Unix only at the moment. - * @param path path to the file - * @param uid user id of the new owner - * @param gid group id of the new owner - */ -export function chownSync(path: string, uid: number, gid: number): void { - sendSync(dispatch.OP_CHOWN, { path, uid, gid }); -} - -/** - * Change owner of a regular file or directory asynchronously. Unix only at the moment. - * @param path path to the file - * @param uid user id of the new owner - * @param gid group id of the new owner - */ -export async function chown( - path: string, - uid: number, - gid: number -): Promise<void> { - await sendAsync(dispatch.OP_CHOWN, { path, uid, gid }); -} diff --git a/js/chown_test.ts b/js/chown_test.ts deleted file mode 100644 index 84106d545..000000000 --- a/js/chown_test.ts +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { testPerm, assertEquals } from "./test_util.ts"; - -// chown on Windows is noop for now, so ignore its testing on Windows -if (Deno.build.os !== "win") { - async function getUidAndGid(): Promise<{ uid: number; gid: number }> { - // get the user ID and group ID of the current process - const uidProc = Deno.run({ - stdout: "piped", - args: ["python", "-c", "import os; print(os.getuid())"] - }); - const gidProc = Deno.run({ - stdout: "piped", - args: ["python", "-c", "import os; print(os.getgid())"] - }); - - assertEquals((await uidProc.status()).code, 0); - assertEquals((await gidProc.status()).code, 0); - const uid = parseInt( - new TextDecoder("utf-8").decode(await uidProc.output()) - ); - const gid = parseInt( - new TextDecoder("utf-8").decode(await gidProc.output()) - ); - - return { uid, gid }; - } - - testPerm({}, async function chownNoWritePermission(): Promise<void> { - const filePath = "chown_test_file.txt"; - try { - await Deno.chown(filePath, 1000, 1000); - } catch (e) { - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - }); - - testPerm( - { run: true, write: true }, - async function chownSyncFileNotExist(): Promise<void> { - const { uid, gid } = await getUidAndGid(); - const filePath = Deno.makeTempDirSync() + "/chown_test_file.txt"; - - try { - Deno.chownSync(filePath, uid, gid); - } catch (e) { - assertEquals(e.kind, Deno.ErrorKind.NotFound); - assertEquals(e.name, "NotFound"); - } - } - ); - - testPerm( - { run: true, write: true }, - async function chownFileNotExist(): Promise<void> { - const { uid, gid } = await getUidAndGid(); - const filePath = (await Deno.makeTempDir()) + "/chown_test_file.txt"; - - try { - await Deno.chown(filePath, uid, gid); - } catch (e) { - assertEquals(e.kind, Deno.ErrorKind.NotFound); - assertEquals(e.name, "NotFound"); - } - } - ); - - testPerm({ write: true }, function chownSyncPermissionDenied(): void { - const enc = new TextEncoder(); - const dirPath = Deno.makeTempDirSync(); - const filePath = dirPath + "/chown_test_file.txt"; - const fileData = enc.encode("Hello"); - Deno.writeFileSync(filePath, fileData); - - try { - // try changing the file's owner to root - Deno.chownSync(filePath, 0, 0); - } catch (e) { - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - Deno.removeSync(dirPath, { recursive: true }); - }); - - testPerm({ write: true }, async function chownPermissionDenied(): Promise< - void - > { - const enc = new TextEncoder(); - const dirPath = await Deno.makeTempDir(); - const filePath = dirPath + "/chown_test_file.txt"; - const fileData = enc.encode("Hello"); - await Deno.writeFile(filePath, fileData); - - try { - // try changing the file's owner to root - await Deno.chown(filePath, 0, 0); - } catch (e) { - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - await Deno.remove(dirPath, { recursive: true }); - }); - - testPerm( - { run: true, write: true }, - async function chownSyncSucceed(): Promise<void> { - // TODO: when a file's owner is actually being changed, - // chown only succeeds if run under priviledged user (root) - // The test script has no such priviledge, so need to find a better way to test this case - const { uid, gid } = await getUidAndGid(); - - const enc = new TextEncoder(); - const dirPath = Deno.makeTempDirSync(); - const filePath = dirPath + "/chown_test_file.txt"; - const fileData = enc.encode("Hello"); - Deno.writeFileSync(filePath, fileData); - - // the test script creates this file with the same uid and gid, - // here chown is a noop so it succeeds under non-priviledged user - Deno.chownSync(filePath, uid, gid); - - Deno.removeSync(dirPath, { recursive: true }); - } - ); - - testPerm({ run: true, write: true }, async function chownSucceed(): Promise< - void - > { - // TODO: same as chownSyncSucceed - const { uid, gid } = await getUidAndGid(); - - const enc = new TextEncoder(); - const dirPath = await Deno.makeTempDir(); - const filePath = dirPath + "/chown_test_file.txt"; - const fileData = enc.encode("Hello"); - await Deno.writeFile(filePath, fileData); - - // the test script creates this file with the same uid and gid, - // here chown is a noop so it succeeds under non-priviledged user - await Deno.chown(filePath, uid, gid); - - Deno.removeSync(dirPath, { recursive: true }); - }); -} diff --git a/js/colors.ts b/js/colors.ts deleted file mode 100644 index 9937bdb57..000000000 --- a/js/colors.ts +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. - -// TODO(kitsonk) Replace with `deno_std/colors/mod.ts` when we can load modules -// which end in `.ts`. - -import { noColor } from "./deno.ts"; - -interface Code { - open: string; - close: string; - regexp: RegExp; -} - -const enabled = !noColor; - -function code(open: number, close: number): Code { - return { - open: `\x1b[${open}m`, - close: `\x1b[${close}m`, - regexp: new RegExp(`\\x1b\\[${close}m`, "g") - }; -} - -function run(str: string, code: Code): string { - return enabled - ? `${code.open}${str.replace(code.regexp, code.open)}${code.close}` - : str; -} - -export function bold(str: string): string { - return run(str, code(1, 22)); -} - -export function yellow(str: string): string { - return run(str, code(33, 39)); -} - -export function cyan(str: string): string { - return run(str, code(36, 39)); -} diff --git a/js/compiler.ts b/js/compiler.ts deleted file mode 100644 index 57e5e3a47..000000000 --- a/js/compiler.ts +++ /dev/null @@ -1,667 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -// TODO(ry) Combine this implementation with //deno_typescript/compiler_main.js - -import "./globals.ts"; -import "./ts_global.d.ts"; - -import { bold, cyan, yellow } from "./colors.ts"; -import { Console } from "./console.ts"; -import { core } from "./core.ts"; -import { Diagnostic, fromTypeScriptDiagnostic } from "./diagnostics.ts"; -import { cwd } from "./dir.ts"; -import * as dispatch from "./dispatch.ts"; -import { sendAsync, sendSync } from "./dispatch_json.ts"; -import * as os from "./os.ts"; -import { TextEncoder } from "./text_encoding.ts"; -import { getMappedModuleName, parseTypeDirectives } from "./type_directives.ts"; -import { assert, notImplemented } from "./util.ts"; -import * as util from "./util.ts"; -import { window } from "./window.ts"; -import { postMessage, workerClose, workerMain } from "./workers.ts"; -import { writeFileSync } from "./write_file.ts"; - -// Warning! The values in this enum are duplicated in cli/msg.rs -// Update carefully! -enum MediaType { - JavaScript = 0, - JSX = 1, - TypeScript = 2, - TSX = 3, - Json = 4, - Unknown = 5 -} - -// Startup boilerplate. This is necessary because the compiler has its own -// snapshot. (It would be great if we could remove these things or centralize -// them somewhere else.) -const console = new Console(core.print); -window.console = console; -window.workerMain = workerMain; -function denoMain(): void { - os.start(true, "TS"); -} -window["denoMain"] = denoMain; - -const ASSETS = "$asset$"; -const OUT_DIR = "$deno$"; - -/** The format of the work message payload coming from the privileged side */ -interface CompilerReq { - rootNames: string[]; - bundle?: string; - // TODO(ry) add compiler config to this interface. - // options: ts.CompilerOptions; - configPath?: string; - config?: string; -} - -interface ConfigureResponse { - ignoredOptions?: string[]; - diagnostics?: ts.Diagnostic[]; -} - -/** Options that either do nothing in Deno, or would cause undesired behavior - * if modified. */ -const ignoredCompilerOptions: readonly string[] = [ - "allowSyntheticDefaultImports", - "baseUrl", - "build", - "composite", - "declaration", - "declarationDir", - "declarationMap", - "diagnostics", - "downlevelIteration", - "emitBOM", - "emitDeclarationOnly", - "esModuleInterop", - "extendedDiagnostics", - "forceConsistentCasingInFileNames", - "help", - "importHelpers", - "incremental", - "inlineSourceMap", - "inlineSources", - "init", - "isolatedModules", - "lib", - "listEmittedFiles", - "listFiles", - "mapRoot", - "maxNodeModuleJsDepth", - "module", - "moduleResolution", - "newLine", - "noEmit", - "noEmitHelpers", - "noEmitOnError", - "noLib", - "noResolve", - "out", - "outDir", - "outFile", - "paths", - "preserveSymlinks", - "preserveWatchOutput", - "pretty", - "rootDir", - "rootDirs", - "showConfig", - "skipDefaultLibCheck", - "skipLibCheck", - "sourceMap", - "sourceRoot", - "stripInternal", - "target", - "traceResolution", - "tsBuildInfoFile", - "types", - "typeRoots", - "version", - "watch" -]; - -/** The shape of the SourceFile that comes from the privileged side */ -interface SourceFileJson { - url: string; - filename: string; - mediaType: MediaType; - sourceCode: string; -} - -/** A self registering abstraction of source files. */ -class SourceFile { - extension!: ts.Extension; - filename!: string; - - /** An array of tuples which represent the imports for the source file. The - * first element is the one that will be requested at compile time, the - * second is the one that should be actually resolved. This provides the - * feature of type directives for Deno. */ - importedFiles?: Array<[string, string]>; - - mediaType!: MediaType; - processed = false; - sourceCode!: string; - tsSourceFile?: ts.SourceFile; - url!: string; - - constructor(json: SourceFileJson) { - if (SourceFile._moduleCache.has(json.url)) { - throw new TypeError("SourceFile already exists"); - } - Object.assign(this, json); - this.extension = getExtension(this.url, this.mediaType); - SourceFile._moduleCache.set(this.url, this); - } - - /** Cache the source file to be able to be retrieved by `moduleSpecifier` and - * `containingFile`. */ - cache(moduleSpecifier: string, containingFile: string): void { - let innerCache = SourceFile._specifierCache.get(containingFile); - if (!innerCache) { - innerCache = new Map(); - SourceFile._specifierCache.set(containingFile, innerCache); - } - innerCache.set(moduleSpecifier, this); - } - - /** Process the imports for the file and return them. */ - imports(): Array<[string, string]> { - if (this.processed) { - throw new Error("SourceFile has already been processed."); - } - assert(this.sourceCode != null); - const preProcessedFileInfo = ts.preProcessFile( - this.sourceCode!, - true, - true - ); - this.processed = true; - const files = (this.importedFiles = [] as Array<[string, string]>); - - function process(references: ts.FileReference[]): void { - for (const { fileName } of references) { - files.push([fileName, fileName]); - } - } - - const { - importedFiles, - referencedFiles, - libReferenceDirectives, - typeReferenceDirectives - } = preProcessedFileInfo; - const typeDirectives = parseTypeDirectives(this.sourceCode); - if (typeDirectives) { - for (const importedFile of importedFiles) { - files.push([ - importedFile.fileName, - getMappedModuleName(importedFile, typeDirectives) - ]); - } - } else { - process(importedFiles); - } - process(referencedFiles); - process(libReferenceDirectives); - process(typeReferenceDirectives); - return files; - } - - /** A cache of all the source files which have been loaded indexed by the - * url. */ - private static _moduleCache: Map<string, SourceFile> = new Map(); - - /** A cache of source files based on module specifiers and containing files - * which is used by the TypeScript compiler to resolve the url */ - private static _specifierCache: Map< - string, - Map<string, SourceFile> - > = new Map(); - - /** Retrieve a `SourceFile` based on a `moduleSpecifier` and `containingFile` - * or return `undefined` if not preset. */ - static getUrl( - moduleSpecifier: string, - containingFile: string - ): string | undefined { - const containingCache = this._specifierCache.get(containingFile); - if (containingCache) { - const sourceFile = containingCache.get(moduleSpecifier); - return sourceFile && sourceFile.url; - } - return undefined; - } - - /** Retrieve a `SourceFile` based on a `url` */ - static get(url: string): SourceFile | undefined { - return this._moduleCache.get(url); - } -} - -interface EmitResult { - emitSkipped: boolean; - diagnostics?: Diagnostic; -} - -/** Ops to Rust to resolve special static assets. */ -function fetchAsset(name: string): string { - return sendSync(dispatch.OP_FETCH_ASSET, { name }); -} - -/** Ops to Rust to resolve and fetch modules meta data. */ -function fetchSourceFiles( - specifiers: string[], - referrer: string -): Promise<SourceFileJson[]> { - util.log("compiler::fetchSourceFiles", { specifiers, referrer }); - return sendAsync(dispatch.OP_FETCH_SOURCE_FILES, { - specifiers, - referrer - }); -} - -/** Recursively process the imports of modules, generating `SourceFile`s of any - * imported files. - * - * Specifiers are supplied in an array of tupples where the first is the - * specifier that will be requested in the code and the second is the specifier - * that should be actually resolved. */ -async function processImports( - specifiers: Array<[string, string]>, - referrer = "" -): Promise<void> { - if (!specifiers.length) { - return; - } - const sources = specifiers.map(([, moduleSpecifier]) => moduleSpecifier); - const sourceFiles = await fetchSourceFiles(sources, referrer); - assert(sourceFiles.length === specifiers.length); - for (let i = 0; i < sourceFiles.length; i++) { - const sourceFileJson = sourceFiles[i]; - const sourceFile = - SourceFile.get(sourceFileJson.url) || new SourceFile(sourceFileJson); - sourceFile.cache(specifiers[i][0], referrer); - if (!sourceFile.processed) { - await processImports(sourceFile.imports(), sourceFile.url); - } - } -} - -/** Utility function to turn the number of bytes into a human readable - * unit */ -function humanFileSize(bytes: number): string { - const thresh = 1000; - if (Math.abs(bytes) < thresh) { - return bytes + " B"; - } - const units = ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; - let u = -1; - do { - bytes /= thresh; - ++u; - } while (Math.abs(bytes) >= thresh && u < units.length - 1); - return `${bytes.toFixed(1)} ${units[u]}`; -} - -/** Ops to rest for caching source map and compiled js */ -function cache(extension: string, moduleId: string, contents: string): void { - util.log("compiler::cache", { extension, moduleId }); - sendSync(dispatch.OP_CACHE, { extension, moduleId, contents }); -} - -const encoder = new TextEncoder(); - -/** Given a fileName and the data, emit the file to the file system. */ -function emitBundle(fileName: string, data: string): void { - // For internal purposes, when trying to emit to `$deno$` just no-op - if (fileName.startsWith("$deno$")) { - console.warn("skipping emitBundle", fileName); - return; - } - const encodedData = encoder.encode(data); - console.log(`Emitting bundle to "${fileName}"`); - writeFileSync(fileName, encodedData); - console.log(`${humanFileSize(encodedData.length)} emitted.`); -} - -/** Returns the TypeScript Extension enum for a given media type. */ -function getExtension(fileName: string, mediaType: MediaType): ts.Extension { - switch (mediaType) { - case MediaType.JavaScript: - return ts.Extension.Js; - case MediaType.JSX: - return ts.Extension.Jsx; - case MediaType.TypeScript: - return fileName.endsWith(".d.ts") ? ts.Extension.Dts : ts.Extension.Ts; - case MediaType.TSX: - return ts.Extension.Tsx; - case MediaType.Json: - return ts.Extension.Json; - case MediaType.Unknown: - default: - throw TypeError("Cannot resolve extension."); - } -} - -class Host implements ts.CompilerHost { - private readonly _options: ts.CompilerOptions = { - allowJs: true, - allowNonTsExtensions: true, - checkJs: false, - esModuleInterop: true, - module: ts.ModuleKind.ESNext, - outDir: OUT_DIR, - resolveJsonModule: true, - sourceMap: true, - stripComments: true, - target: ts.ScriptTarget.ESNext, - jsx: ts.JsxEmit.React - }; - - private _getAsset(filename: string): SourceFile { - const sourceFile = SourceFile.get(filename); - if (sourceFile) { - return sourceFile; - } - const url = filename.split("/").pop()!; - const assetName = url.includes(".") ? url : `${url}.d.ts`; - const sourceCode = fetchAsset(assetName); - return new SourceFile({ - url, - filename, - mediaType: MediaType.TypeScript, - sourceCode - }); - } - - /* Deno specific APIs */ - - /** Provides the `ts.HostCompiler` interface for Deno. - * - * @param _bundle Set to a string value to configure the host to write out a - * bundle instead of caching individual files. - */ - constructor(private _bundle?: string) { - if (this._bundle) { - // options we need to change when we are generating a bundle - const bundlerOptions: ts.CompilerOptions = { - module: ts.ModuleKind.AMD, - inlineSourceMap: true, - outDir: undefined, - outFile: `${OUT_DIR}/bundle.js`, - sourceMap: false - }; - Object.assign(this._options, bundlerOptions); - } - } - - /** Take a configuration string, parse it, and use it to merge with the - * compiler's configuration options. The method returns an array of compiler - * options which were ignored, or `undefined`. */ - configure(path: string, configurationText: string): ConfigureResponse { - util.log("compiler::host.configure", path); - const { config, error } = ts.parseConfigFileTextToJson( - path, - configurationText - ); - if (error) { - return { diagnostics: [error] }; - } - const { options, errors } = ts.convertCompilerOptionsFromJson( - config.compilerOptions, - cwd() - ); - const ignoredOptions: string[] = []; - for (const key of Object.keys(options)) { - if ( - ignoredCompilerOptions.includes(key) && - (!(key in this._options) || options[key] !== this._options[key]) - ) { - ignoredOptions.push(key); - delete options[key]; - } - } - Object.assign(this._options, options); - return { - ignoredOptions: ignoredOptions.length ? ignoredOptions : undefined, - diagnostics: errors.length ? errors : undefined - }; - } - - /* TypeScript CompilerHost APIs */ - - fileExists(_fileName: string): boolean { - return notImplemented(); - } - - getCanonicalFileName(fileName: string): string { - return fileName; - } - - getCompilationSettings(): ts.CompilerOptions { - util.log("compiler::host.getCompilationSettings()"); - return this._options; - } - - getCurrentDirectory(): string { - return ""; - } - - getDefaultLibFileName(_options: ts.CompilerOptions): string { - return ASSETS + "/lib.deno_runtime.d.ts"; - } - - getNewLine(): string { - return "\n"; - } - - getSourceFile( - fileName: string, - languageVersion: ts.ScriptTarget, - onError?: (message: string) => void, - shouldCreateNewSourceFile?: boolean - ): ts.SourceFile | undefined { - util.log("compiler::host.getSourceFile", fileName); - try { - assert(!shouldCreateNewSourceFile); - const sourceFile = fileName.startsWith(ASSETS) - ? this._getAsset(fileName) - : SourceFile.get(fileName); - assert(sourceFile != null); - if (!sourceFile!.tsSourceFile) { - sourceFile!.tsSourceFile = ts.createSourceFile( - fileName, - sourceFile!.sourceCode, - languageVersion - ); - } - return sourceFile!.tsSourceFile; - } catch (e) { - if (onError) { - onError(String(e)); - } else { - throw e; - } - return undefined; - } - } - - readFile(_fileName: string): string | undefined { - return notImplemented(); - } - - resolveModuleNames( - moduleNames: string[], - containingFile: string - ): Array<ts.ResolvedModuleFull | undefined> { - util.log("compiler::host.resolveModuleNames", { - moduleNames, - containingFile - }); - return moduleNames.map(specifier => { - const url = SourceFile.getUrl(specifier, containingFile); - const sourceFile = specifier.startsWith(ASSETS) - ? this._getAsset(specifier) - : url - ? SourceFile.get(url) - : undefined; - if (!sourceFile) { - return undefined; - } - return { - resolvedFileName: sourceFile.url, - isExternalLibraryImport: specifier.startsWith(ASSETS), - extension: sourceFile.extension - }; - }); - } - - useCaseSensitiveFileNames(): boolean { - return true; - } - - writeFile( - fileName: string, - data: string, - _writeByteOrderMark: boolean, - onError?: (message: string) => void, - sourceFiles?: readonly ts.SourceFile[] - ): void { - util.log("compiler::host.writeFile", fileName); - try { - if (this._bundle) { - emitBundle(this._bundle, data); - } else { - assert(sourceFiles != null && sourceFiles.length == 1); - const url = sourceFiles![0].fileName; - const sourceFile = SourceFile.get(url); - - if (sourceFile) { - // NOTE: If it's a `.json` file we don't want to write it to disk. - // JSON files are loaded and used by TS compiler to check types, but we don't want - // to emit them to disk because output file is the same as input file. - if (sourceFile.extension === ts.Extension.Json) { - return; - } - - // NOTE: JavaScript files are only emitted to disk if `checkJs` option in on - if ( - sourceFile.extension === ts.Extension.Js && - !this._options.checkJs - ) { - return; - } - } - - if (fileName.endsWith(".map")) { - // Source Map - cache(".map", url, data); - } else if (fileName.endsWith(".js") || fileName.endsWith(".json")) { - // Compiled JavaScript - cache(".js", url, data); - } else { - assert(false, "Trying to cache unhandled file type " + fileName); - } - } - } catch (e) { - if (onError) { - onError(String(e)); - } else { - throw e; - } - } - } -} - -// provide the "main" function that will be called by the privileged side when -// lazy instantiating the compiler web worker -window.compilerMain = function compilerMain(): void { - // workerMain should have already been called since a compiler is a worker. - window.onmessage = async ({ data }: { data: CompilerReq }): Promise<void> => { - const { rootNames, configPath, config, bundle } = data; - util.log(">>> compile start", { rootNames, bundle }); - - // This will recursively analyse all the code for other imports, requesting - // those from the privileged side, populating the in memory cache which - // will be used by the host, before resolving. - await processImports(rootNames.map(rootName => [rootName, rootName])); - - const host = new Host(bundle); - let emitSkipped = true; - let diagnostics: ts.Diagnostic[] | undefined; - - // if there is a configuration supplied, we need to parse that - if (config && config.length && configPath) { - const configResult = host.configure(configPath, config); - const ignoredOptions = configResult.ignoredOptions; - diagnostics = configResult.diagnostics; - if (ignoredOptions) { - console.warn( - yellow(`Unsupported compiler options in "${configPath}"\n`) + - cyan(` The following options were ignored:\n`) + - ` ${ignoredOptions - .map((value): string => bold(value)) - .join(", ")}` - ); - } - } - - // if there was a configuration and no diagnostics with it, we will continue - // to generate the program and possibly emit it. - if (!diagnostics || (diagnostics && diagnostics.length === 0)) { - const options = host.getCompilationSettings(); - const program = ts.createProgram(rootNames, options, host); - - diagnostics = ts.getPreEmitDiagnostics(program).filter( - ({ code }): boolean => { - // TS1308: 'await' expression is only allowed within an async - // function. - if (code === 1308) return false; - // TS2691: An import path cannot end with a '.ts' extension. Consider - // importing 'bad-module' instead. - if (code === 2691) return false; - // TS5009: Cannot find the common subdirectory path for the input files. - if (code === 5009) return false; - // TS5055: Cannot write file - // 'http://localhost:4545/tests/subdir/mt_application_x_javascript.j4.js' - // because it would overwrite input file. - if (code === 5055) return false; - // TypeScript is overly opinionated that only CommonJS modules kinds can - // support JSON imports. Allegedly this was fixed in - // Microsoft/TypeScript#26825 but that doesn't seem to be working here, - // so we will ignore complaints about this compiler setting. - if (code === 5070) return false; - return true; - } - ); - - // We will only proceed with the emit if there are no diagnostics. - if (diagnostics && diagnostics.length === 0) { - if (bundle) { - console.log(`Bundling "${bundle}"`); - } - const emitResult = program.emit(); - emitSkipped = emitResult.emitSkipped; - // emitResult.diagnostics is `readonly` in TS3.5+ and can't be assigned - // without casting. - diagnostics = emitResult.diagnostics as ts.Diagnostic[]; - } - } - - const result: EmitResult = { - emitSkipped, - diagnostics: diagnostics.length - ? fromTypeScriptDiagnostic(diagnostics) - : undefined - }; - - postMessage(result); - - util.log("<<< compile end", { rootNames, bundle }); - - // The compiler isolate exits after a single message. - workerClose(); - }; -}; diff --git a/js/console.ts b/js/console.ts deleted file mode 100644 index 9f0ce4bd6..000000000 --- a/js/console.ts +++ /dev/null @@ -1,790 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { isTypedArray } from "./util.ts"; -import { TypedArray } from "./types.ts"; -import { TextEncoder } from "./text_encoding.ts"; -import { File, stdout } from "./files.ts"; -import { cliTable } from "./console_table.ts"; - -type ConsoleContext = Set<unknown>; -type ConsoleOptions = Partial<{ - showHidden: boolean; - depth: number; - colors: boolean; - indentLevel: number; -}>; - -// Default depth of logging nested objects -const DEFAULT_MAX_DEPTH = 4; - -// Number of elements an object must have before it's displayed in appreviated -// form. -const OBJ_ABBREVIATE_SIZE = 5; - -const STR_ABBREVIATE_SIZE = 100; - -// Char codes -const CHAR_PERCENT = 37; /* % */ -const CHAR_LOWERCASE_S = 115; /* s */ -const CHAR_LOWERCASE_D = 100; /* d */ -const CHAR_LOWERCASE_I = 105; /* i */ -const CHAR_LOWERCASE_F = 102; /* f */ -const CHAR_LOWERCASE_O = 111; /* o */ -const CHAR_UPPERCASE_O = 79; /* O */ -const CHAR_LOWERCASE_C = 99; /* c */ -export class CSI { - static kClear = "\x1b[1;1H"; - static kClearScreenDown = "\x1b[0J"; -} - -/* eslint-disable @typescript-eslint/no-use-before-define */ - -function cursorTo(stream: File, _x: number, _y?: number): void { - const uint8 = new TextEncoder().encode(CSI.kClear); - stream.write(uint8); -} - -function clearScreenDown(stream: File): void { - const uint8 = new TextEncoder().encode(CSI.kClearScreenDown); - stream.write(uint8); -} - -function getClassInstanceName(instance: unknown): string { - if (typeof instance !== "object") { - return ""; - } - if (!instance) { - return ""; - } - - const proto = Object.getPrototypeOf(instance); - if (proto && proto.constructor) { - return proto.constructor.name; // could be "Object" or "Array" - } - - return ""; -} - -function createFunctionString(value: Function, _ctx: ConsoleContext): string { - // Might be Function/AsyncFunction/GeneratorFunction - const cstrName = Object.getPrototypeOf(value).constructor.name; - if (value.name && value.name !== "anonymous") { - // from MDN spec - return `[${cstrName}: ${value.name}]`; - } - return `[${cstrName}]`; -} - -interface IterablePrintConfig<T> { - typeName: string; - displayName: string; - delims: [string, string]; - entryHandler: ( - entry: T, - ctx: ConsoleContext, - level: number, - maxLevel: number - ) => string; -} - -function createIterableString<T>( - value: Iterable<T>, - ctx: ConsoleContext, - level: number, - maxLevel: number, - config: IterablePrintConfig<T> -): string { - if (level >= maxLevel) { - return `[${config.typeName}]`; - } - ctx.add(value); - - const entries: string[] = []; - // In cases e.g. Uint8Array.prototype - try { - for (const el of value) { - entries.push(config.entryHandler(el, ctx, level + 1, maxLevel)); - } - } catch (e) {} - ctx.delete(value); - const iPrefix = `${config.displayName ? config.displayName + " " : ""}`; - const iContent = entries.length === 0 ? "" : ` ${entries.join(", ")} `; - return `${iPrefix}${config.delims[0]}${iContent}${config.delims[1]}`; -} - -function stringify( - value: unknown, - ctx: ConsoleContext, - level: number, - maxLevel: number -): string { - switch (typeof value) { - case "string": - return value; - case "number": - // Special handling of -0 - return Object.is(value, -0) ? "-0" : `${value}`; - case "boolean": - case "undefined": - case "symbol": - return String(value); - case "bigint": - return `${value}n`; - case "function": - return createFunctionString(value as Function, ctx); - case "object": - if (value === null) { - return "null"; - } - - if (ctx.has(value)) { - return "[Circular]"; - } - - return createObjectString(value, ctx, level, maxLevel); - default: - return "[Not Implemented]"; - } -} - -// Print strings when they are inside of arrays or objects with quotes -function stringifyWithQuotes( - value: unknown, - ctx: ConsoleContext, - level: number, - maxLevel: number -): string { - switch (typeof value) { - case "string": - const trunc = - value.length > STR_ABBREVIATE_SIZE - ? value.slice(0, STR_ABBREVIATE_SIZE) + "..." - : value; - return JSON.stringify(trunc); - default: - return stringify(value, ctx, level, maxLevel); - } -} - -function createArrayString( - value: unknown[], - ctx: ConsoleContext, - level: number, - maxLevel: number -): string { - const printConfig: IterablePrintConfig<unknown> = { - typeName: "Array", - displayName: "", - delims: ["[", "]"], - entryHandler: (el, ctx, level, maxLevel): string => - stringifyWithQuotes(el, ctx, level + 1, maxLevel) - }; - return createIterableString(value, ctx, level, maxLevel, printConfig); -} - -function createTypedArrayString( - typedArrayName: string, - value: TypedArray, - ctx: ConsoleContext, - level: number, - maxLevel: number -): string { - const printConfig: IterablePrintConfig<unknown> = { - typeName: typedArrayName, - displayName: typedArrayName, - delims: ["[", "]"], - entryHandler: (el, ctx, level, maxLevel): string => - stringifyWithQuotes(el, ctx, level + 1, maxLevel) - }; - return createIterableString(value, ctx, level, maxLevel, printConfig); -} - -function createSetString( - value: Set<unknown>, - ctx: ConsoleContext, - level: number, - maxLevel: number -): string { - const printConfig: IterablePrintConfig<unknown> = { - typeName: "Set", - displayName: "Set", - delims: ["{", "}"], - entryHandler: (el, ctx, level, maxLevel): string => - stringifyWithQuotes(el, ctx, level + 1, maxLevel) - }; - return createIterableString(value, ctx, level, maxLevel, printConfig); -} - -function createMapString( - value: Map<unknown, unknown>, - ctx: ConsoleContext, - level: number, - maxLevel: number -): string { - const printConfig: IterablePrintConfig<[unknown, unknown]> = { - typeName: "Map", - displayName: "Map", - delims: ["{", "}"], - entryHandler: (el, ctx, level, maxLevel): string => { - const [key, val] = el; - return `${stringifyWithQuotes( - key, - ctx, - level + 1, - maxLevel - )} => ${stringifyWithQuotes(val, ctx, level + 1, maxLevel)}`; - } - }; - return createIterableString(value, ctx, level, maxLevel, printConfig); -} - -function createWeakSetString(): string { - return "WeakSet { [items unknown] }"; // as seen in Node -} - -function createWeakMapString(): string { - return "WeakMap { [items unknown] }"; // as seen in Node -} - -function createDateString(value: Date): string { - // without quotes, ISO format - return value.toISOString(); -} - -function createRegExpString(value: RegExp): string { - return value.toString(); -} - -/* eslint-disable @typescript-eslint/ban-types */ - -function createStringWrapperString(value: String): string { - return `[String: "${value.toString()}"]`; -} - -function createBooleanWrapperString(value: Boolean): string { - return `[Boolean: ${value.toString()}]`; -} - -function createNumberWrapperString(value: Number): string { - return `[Number: ${value.toString()}]`; -} - -/* eslint-enable @typescript-eslint/ban-types */ - -// TODO: Promise, requires v8 bindings to get info -// TODO: Proxy - -function createRawObjectString( - value: { [key: string]: unknown }, - ctx: ConsoleContext, - level: number, - maxLevel: number -): string { - if (level >= maxLevel) { - return "[Object]"; - } - ctx.add(value); - - let baseString = ""; - - const className = getClassInstanceName(value); - let shouldShowClassName = false; - if (className && className !== "Object" && className !== "anonymous") { - shouldShowClassName = true; - } - const keys = Object.keys(value); - const entries: string[] = keys.map( - (key): string => { - if (keys.length > OBJ_ABBREVIATE_SIZE) { - return key; - } else { - return `${key}: ${stringifyWithQuotes( - value[key], - ctx, - level + 1, - maxLevel - )}`; - } - } - ); - - ctx.delete(value); - - if (entries.length === 0) { - baseString = "{}"; - } else { - baseString = `{ ${entries.join(", ")} }`; - } - - if (shouldShowClassName) { - baseString = `${className} ${baseString}`; - } - - return baseString; -} - -function createObjectString( - value: {}, - ...args: [ConsoleContext, number, number] -): string { - if (customInspect in value && typeof value[customInspect] === "function") { - return String(value[customInspect]!()); - } else if (value instanceof Error) { - return String(value.stack); - } else if (Array.isArray(value)) { - return createArrayString(value, ...args); - } else if (value instanceof Number) { - return createNumberWrapperString(value); - } else if (value instanceof Boolean) { - return createBooleanWrapperString(value); - } else if (value instanceof String) { - return createStringWrapperString(value); - } else if (value instanceof RegExp) { - return createRegExpString(value); - } else if (value instanceof Date) { - return createDateString(value); - } else if (value instanceof Set) { - return createSetString(value, ...args); - } else if (value instanceof Map) { - return createMapString(value, ...args); - } else if (value instanceof WeakSet) { - return createWeakSetString(); - } else if (value instanceof WeakMap) { - return createWeakMapString(); - } else if (isTypedArray(value)) { - return createTypedArrayString( - Object.getPrototypeOf(value).constructor.name, - value, - ...args - ); - } else { - // Otherwise, default object formatting - return createRawObjectString(value, ...args); - } -} - -/** TODO Do not expose this from "deno" namespace. - * @internal - */ -export function stringifyArgs( - args: unknown[], - options: ConsoleOptions = {} -): string { - const first = args[0]; - let a = 0; - let str = ""; - let join = ""; - - if (typeof first === "string") { - let tempStr: string; - let lastPos = 0; - - for (let i = 0; i < first.length - 1; i++) { - if (first.charCodeAt(i) === CHAR_PERCENT) { - const nextChar = first.charCodeAt(++i); - if (a + 1 !== args.length) { - switch (nextChar) { - case CHAR_LOWERCASE_S: - // format as a string - tempStr = String(args[++a]); - break; - case CHAR_LOWERCASE_D: - case CHAR_LOWERCASE_I: - // format as an integer - const tempInteger = args[++a]; - if (typeof tempInteger === "bigint") { - tempStr = `${tempInteger}n`; - } else if (typeof tempInteger === "symbol") { - tempStr = "NaN"; - } else { - tempStr = `${parseInt(String(tempInteger), 10)}`; - } - break; - case CHAR_LOWERCASE_F: - // format as a floating point value - const tempFloat = args[++a]; - if (typeof tempFloat === "symbol") { - tempStr = "NaN"; - } else { - tempStr = `${parseFloat(String(tempFloat))}`; - } - break; - case CHAR_LOWERCASE_O: - case CHAR_UPPERCASE_O: - // format as an object - tempStr = stringify( - args[++a], - new Set<unknown>(), - 0, - options.depth != undefined ? options.depth : DEFAULT_MAX_DEPTH - ); - break; - case CHAR_PERCENT: - str += first.slice(lastPos, i); - lastPos = i + 1; - continue; - case CHAR_LOWERCASE_C: - // TODO: applies CSS style rules to the output string as specified - continue; - default: - // any other character is not a correct placeholder - continue; - } - - if (lastPos !== i - 1) { - str += first.slice(lastPos, i - 1); - } - - str += tempStr; - lastPos = i + 1; - } else if (nextChar === CHAR_PERCENT) { - str += first.slice(lastPos, i); - lastPos = i + 1; - } - } - } - - if (lastPos !== 0) { - a++; - join = " "; - if (lastPos < first.length) { - str += first.slice(lastPos); - } - } - } - - while (a < args.length) { - const value = args[a]; - str += join; - if (typeof value === "string") { - str += value; - } else { - // use default maximum depth for null or undefined argument - str += stringify( - value, - new Set<unknown>(), - 0, - options.depth != undefined ? options.depth : DEFAULT_MAX_DEPTH - ); - } - join = " "; - a++; - } - - const { indentLevel } = options; - if (indentLevel != null && indentLevel > 0) { - const groupIndent = " ".repeat(indentLevel); - if (str.indexOf("\n") !== -1) { - str = str.replace(/\n/g, `\n${groupIndent}`); - } - str = groupIndent + str; - } - - return str; -} - -type PrintFunc = (x: string, isErr?: boolean) => void; - -const countMap = new Map<string, number>(); -const timerMap = new Map<string, number>(); -const isConsoleInstance = Symbol("isConsoleInstance"); - -export class Console { - indentLevel: number; - [isConsoleInstance] = false; - - /** @internal */ - constructor(private printFunc: PrintFunc) { - this.indentLevel = 0; - this[isConsoleInstance] = true; - - // ref https://console.spec.whatwg.org/#console-namespace - // For historical web-compatibility reasons, the namespace object for - // console must have as its [[Prototype]] an empty object, created as if - // by ObjectCreate(%ObjectPrototype%), instead of %ObjectPrototype%. - const console = Object.create({}) as Console; - Object.assign(console, this); - return console; - } - - /** Writes the arguments to stdout */ - log = (...args: unknown[]): void => { - this.printFunc( - stringifyArgs(args, { - indentLevel: this.indentLevel - }) + "\n", - false - ); - }; - - /** Writes the arguments to stdout */ - debug = this.log; - /** Writes the arguments to stdout */ - info = this.log; - - /** Writes the properties of the supplied `obj` to stdout */ - dir = (obj: unknown, options: ConsoleOptions = {}): void => { - this.printFunc(stringifyArgs([obj], options) + "\n", false); - }; - - /** From MDN: - * Displays an interactive tree of the descendant elements of - * the specified XML/HTML element. If it is not possible to display - * as an element the JavaScript Object view is shown instead. - * The output is presented as a hierarchical listing of expandable - * nodes that let you see the contents of child nodes. - * - * Since we write to stdout, we can't display anything interactive - * we just fall back to `console.dir`. - */ - dirxml = this.dir; - - /** Writes the arguments to stdout */ - warn = (...args: unknown[]): void => { - this.printFunc( - stringifyArgs(args, { - indentLevel: this.indentLevel - }) + "\n", - true - ); - }; - - /** Writes the arguments to stdout */ - error = this.warn; - - /** Writes an error message to stdout if the assertion is `false`. If the - * assertion is `true`, nothing happens. - * - * ref: https://console.spec.whatwg.org/#assert - */ - assert = (condition = false, ...args: unknown[]): void => { - if (condition) { - return; - } - - if (args.length === 0) { - this.error("Assertion failed"); - return; - } - - const [first, ...rest] = args; - - if (typeof first === "string") { - this.error(`Assertion failed: ${first}`, ...rest); - return; - } - - this.error(`Assertion failed:`, ...args); - }; - - count = (label = "default"): void => { - label = String(label); - - if (countMap.has(label)) { - const current = countMap.get(label) || 0; - countMap.set(label, current + 1); - } else { - countMap.set(label, 1); - } - - this.info(`${label}: ${countMap.get(label)}`); - }; - - countReset = (label = "default"): void => { - label = String(label); - - if (countMap.has(label)) { - countMap.set(label, 0); - } else { - this.warn(`Count for '${label}' does not exist`); - } - }; - - table = (data: unknown, properties?: string[]): void => { - if (properties !== undefined && !Array.isArray(properties)) { - throw new Error( - "The 'properties' argument must be of type Array. " + - "Received type string" - ); - } - - if (data === null || typeof data !== "object") { - return this.log(data); - } - - const objectValues: { [key: string]: string[] } = {}; - const indexKeys: string[] = []; - const values: string[] = []; - - const stringifyValue = (value: unknown): string => - stringifyWithQuotes(value, new Set<unknown>(), 0, 1); - const toTable = (header: string[], body: string[][]): void => - this.log(cliTable(header, body)); - const createColumn = (value: unknown, shift?: number): string[] => [ - ...(shift ? [...new Array(shift)].map((): string => "") : []), - stringifyValue(value) - ]; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let resultData: any; - const isSet = data instanceof Set; - const isMap = data instanceof Map; - const valuesKey = "Values"; - const indexKey = isSet || isMap ? "(iteration index)" : "(index)"; - - if (data instanceof Set) { - resultData = [...data]; - } else if (data instanceof Map) { - let idx = 0; - resultData = {}; - - data.forEach( - (v: unknown, k: unknown): void => { - resultData[idx] = { Key: k, Values: v }; - idx++; - } - ); - } else { - resultData = data!; - } - - Object.keys(resultData).forEach( - (k, idx): void => { - const value: unknown = resultData[k]!; - - if (value !== null && typeof value === "object") { - Object.entries(value as { [key: string]: unknown }).forEach( - ([k, v]): void => { - if (properties && !properties.includes(k)) { - return; - } - - if (objectValues[k]) { - objectValues[k].push(stringifyValue(v)); - } else { - objectValues[k] = createColumn(v, idx); - } - } - ); - - values.push(""); - } else { - values.push(stringifyValue(value)); - } - - indexKeys.push(k); - } - ); - - const headerKeys = Object.keys(objectValues); - const bodyValues = Object.values(objectValues); - const header = [ - indexKey, - ...(properties || [ - ...headerKeys, - !isMap && values.length > 0 && valuesKey - ]) - ].filter(Boolean) as string[]; - const body = [indexKeys, ...bodyValues, values]; - - toTable(header, body); - }; - - time = (label = "default"): void => { - label = String(label); - - if (timerMap.has(label)) { - this.warn(`Timer '${label}' already exists`); - return; - } - - timerMap.set(label, Date.now()); - }; - - timeLog = (label = "default", ...args: unknown[]): void => { - label = String(label); - - if (!timerMap.has(label)) { - this.warn(`Timer '${label}' does not exists`); - return; - } - - const startTime = timerMap.get(label) as number; - const duration = Date.now() - startTime; - - this.info(`${label}: ${duration}ms`, ...args); - }; - - timeEnd = (label = "default"): void => { - label = String(label); - - if (!timerMap.has(label)) { - this.warn(`Timer '${label}' does not exists`); - return; - } - - const startTime = timerMap.get(label) as number; - timerMap.delete(label); - const duration = Date.now() - startTime; - - this.info(`${label}: ${duration}ms`); - }; - - group = (...label: unknown[]): void => { - if (label.length > 0) { - this.log(...label); - } - this.indentLevel += 2; - }; - - groupCollapsed = this.group; - - groupEnd = (): void => { - if (this.indentLevel > 0) { - this.indentLevel -= 2; - } - }; - - clear = (): void => { - this.indentLevel = 0; - cursorTo(stdout, 0, 0); - clearScreenDown(stdout); - }; - - trace = (...args: unknown[]): void => { - const message = stringifyArgs(args, { indentLevel: 0 }); - const err = { - name: "Trace", - message - }; - // @ts-ignore - Error.captureStackTrace(err, this.trace); - this.error((err as Error).stack); - }; - - static [Symbol.hasInstance](instance: Console): boolean { - return instance[isConsoleInstance]; - } -} - -/** A symbol which can be used as a key for a custom method which will be called - * when `Deno.inspect()` is called, or when the object is logged to the console. - */ -export const customInspect = Symbol.for("Deno.customInspect"); - -/** - * `inspect()` converts input into string that has the same format - * as printed by `console.log(...)`; - */ -export function inspect(value: unknown, options?: ConsoleOptions): string { - const opts = options || {}; - if (typeof value === "string") { - return value; - } else { - return stringify( - value, - new Set<unknown>(), - 0, - opts.depth != undefined ? opts.depth : DEFAULT_MAX_DEPTH - ); - } -} diff --git a/js/console_table.ts b/js/console_table.ts deleted file mode 100644 index d74dc0127..000000000 --- a/js/console_table.ts +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. MIT license. -// Forked from Node's lib/internal/cli_table.js - -import { TextEncoder } from "./text_encoding.ts"; -import { hasOwnProperty } from "./util.ts"; - -const encoder = new TextEncoder(); - -const tableChars = { - middleMiddle: "─", - rowMiddle: "┼", - topRight: "┐", - topLeft: "┌", - leftMiddle: "├", - topMiddle: "┬", - bottomRight: "┘", - bottomLeft: "└", - bottomMiddle: "┴", - rightMiddle: "┤", - left: "│ ", - right: " │", - middle: " │ " -}; - -const colorRegExp = /\u001b\[\d\d?m/g; - -function removeColors(str: string): string { - return str.replace(colorRegExp, ""); -} - -function countBytes(str: string): number { - const normalized = removeColors(String(str)).normalize("NFC"); - - return encoder.encode(normalized).byteLength; -} - -function renderRow(row: string[], columnWidths: number[]): string { - let out = tableChars.left; - for (let i = 0; i < row.length; i++) { - const cell = row[i]; - const len = countBytes(cell); - const needed = (columnWidths[i] - len) / 2; - // round(needed) + ceil(needed) will always add up to the amount - // of spaces we need while also left justifying the output. - out += `${" ".repeat(needed)}${cell}${" ".repeat(Math.ceil(needed))}`; - if (i !== row.length - 1) { - out += tableChars.middle; - } - } - out += tableChars.right; - return out; -} - -export function cliTable(head: string[], columns: string[][]): string { - const rows: string[][] = []; - const columnWidths = head.map((h: string): number => countBytes(h)); - const longestColumn = columns.reduce( - (n: number, a: string[]): number => Math.max(n, a.length), - 0 - ); - - for (let i = 0; i < head.length; i++) { - const column = columns[i]; - for (let j = 0; j < longestColumn; j++) { - if (rows[j] === undefined) { - rows[j] = []; - } - const value = (rows[j][i] = hasOwnProperty(column, j) ? column[j] : ""); - const width = columnWidths[i] || 0; - const counted = countBytes(value); - columnWidths[i] = Math.max(width, counted); - } - } - - const divider = columnWidths.map( - (i: number): string => tableChars.middleMiddle.repeat(i + 2) - ); - - let result = - `${tableChars.topLeft}${divider.join(tableChars.topMiddle)}` + - `${tableChars.topRight}\n${renderRow(head, columnWidths)}\n` + - `${tableChars.leftMiddle}${divider.join(tableChars.rowMiddle)}` + - `${tableChars.rightMiddle}\n`; - - for (const row of rows) { - result += `${renderRow(row, columnWidths)}\n`; - } - - result += - `${tableChars.bottomLeft}${divider.join(tableChars.bottomMiddle)}` + - tableChars.bottomRight; - - return result; -} diff --git a/js/console_test.ts b/js/console_test.ts deleted file mode 100644 index 903e65a82..000000000 --- a/js/console_test.ts +++ /dev/null @@ -1,698 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals, test } from "./test_util.ts"; - -// Some of these APIs aren't exposed in the types and so we have to cast to any -// in order to "trick" TypeScript. -const { - Console, - customInspect, - stringifyArgs, - inspect, - write, - stdout - // eslint-disable-next-line @typescript-eslint/no-explicit-any -} = Deno as any; - -function stringify(...args: unknown[]): string { - return stringifyArgs(args).replace(/\n$/, ""); -} - -// test cases from web-platform-tests -// via https://github.com/web-platform-tests/wpt/blob/master/console/console-is-a-namespace.any.js -test(function consoleShouldBeANamespace(): void { - const prototype1 = Object.getPrototypeOf(console); - const prototype2 = Object.getPrototypeOf(prototype1); - - assertEquals(Object.getOwnPropertyNames(prototype1).length, 0); - assertEquals(prototype2, Object.prototype); -}); - -test(function consoleHasRightInstance(): void { - assert(console instanceof Console); - assertEquals({} instanceof Console, false); -}); - -test(function consoleTestAssertShouldNotThrowError(): void { - console.assert(true); - - let hasThrown = undefined; - try { - console.assert(false); - hasThrown = false; - } catch { - hasThrown = true; - } - assertEquals(hasThrown, false); -}); - -test(function consoleTestStringifyComplexObjects(): void { - assertEquals(stringify("foo"), "foo"); - assertEquals(stringify(["foo", "bar"]), `[ "foo", "bar" ]`); - assertEquals(stringify({ foo: "bar" }), `{ foo: "bar" }`); -}); - -test(function consoleTestStringifyLongStrings(): void { - const veryLongString = "a".repeat(200); - // If we stringify an object containing the long string, it gets abbreviated. - let actual = stringify({ veryLongString }); - assert(actual.includes("...")); - assert(actual.length < 200); - // However if we stringify the string itself, we get it exactly. - actual = stringify(veryLongString); - assertEquals(actual, veryLongString); -}); - -/* eslint-disable @typescript-eslint/explicit-function-return-type */ -test(function consoleTestStringifyCircular(): void { - class Base { - a = 1; - m1() {} - } - - class Extended extends Base { - b = 2; - m2() {} - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const nestedObj: any = { - num: 1, - bool: true, - str: "a", - method() {}, - async asyncMethod() {}, - *generatorMethod() {}, - un: undefined, - nu: null, - arrowFunc: () => {}, - extendedClass: new Extended(), - nFunc: new Function(), - extendedCstr: Extended - }; - - const circularObj = { - num: 2, - bool: false, - str: "b", - method() {}, - un: undefined, - nu: null, - nested: nestedObj, - emptyObj: {}, - arr: [1, "s", false, null, nestedObj], - baseClass: new Base() - }; - - nestedObj.o = circularObj; - const nestedObjExpected = `{ num, bool, str, method, asyncMethod, generatorMethod, un, nu, arrowFunc, extendedClass, nFunc, extendedCstr, o }`; - - assertEquals(stringify(1), "1"); - assertEquals(stringify(-0), "-0"); - assertEquals(stringify(1n), "1n"); - assertEquals(stringify("s"), "s"); - assertEquals(stringify(false), "false"); - assertEquals(stringify(new Number(1)), "[Number: 1]"); - assertEquals(stringify(new Boolean(true)), "[Boolean: true]"); - assertEquals(stringify(new String("deno")), `[String: "deno"]`); - assertEquals(stringify(/[0-9]*/), "/[0-9]*/"); - assertEquals( - stringify(new Date("2018-12-10T02:26:59.002Z")), - "2018-12-10T02:26:59.002Z" - ); - assertEquals(stringify(new Set([1, 2, 3])), "Set { 1, 2, 3 }"); - assertEquals( - stringify(new Map([[1, "one"], [2, "two"]])), - `Map { 1 => "one", 2 => "two" }` - ); - assertEquals(stringify(new WeakSet()), "WeakSet { [items unknown] }"); - assertEquals(stringify(new WeakMap()), "WeakMap { [items unknown] }"); - assertEquals(stringify(Symbol(1)), "Symbol(1)"); - assertEquals(stringify(null), "null"); - assertEquals(stringify(undefined), "undefined"); - assertEquals(stringify(new Extended()), "Extended { a: 1, b: 2 }"); - assertEquals(stringify(function f(): void {}), "[Function: f]"); - assertEquals( - stringify(async function af(): Promise<void> {}), - "[AsyncFunction: af]" - ); - assertEquals(stringify(function* gf() {}), "[GeneratorFunction: gf]"); - assertEquals( - stringify(async function* agf() {}), - "[AsyncGeneratorFunction: agf]" - ); - assertEquals(stringify(new Uint8Array([1, 2, 3])), "Uint8Array [ 1, 2, 3 ]"); - assertEquals(stringify(Uint8Array.prototype), "TypedArray []"); - assertEquals( - stringify({ a: { b: { c: { d: new Set([1]) } } } }), - "{ a: { b: { c: { d: [Set] } } } }" - ); - assertEquals(stringify(nestedObj), nestedObjExpected); - assertEquals(stringify(JSON), "{}"); - assertEquals( - stringify(console), - "{ printFunc, log, debug, info, dir, dirxml, warn, error, assert, count, countReset, table, time, timeLog, timeEnd, group, groupCollapsed, groupEnd, clear, trace, indentLevel }" - ); - // test inspect is working the same - assertEquals(inspect(nestedObj), nestedObjExpected); -}); -/* eslint-enable @typescript-eslint/explicit-function-return-type */ - -test(function consoleTestStringifyWithDepth(): void { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const nestedObj: any = { a: { b: { c: { d: { e: { f: 42 } } } } } }; - assertEquals( - stringifyArgs([nestedObj], { depth: 3 }), - "{ a: { b: { c: [Object] } } }" - ); - assertEquals( - stringifyArgs([nestedObj], { depth: 4 }), - "{ a: { b: { c: { d: [Object] } } } }" - ); - assertEquals(stringifyArgs([nestedObj], { depth: 0 }), "[Object]"); - assertEquals( - stringifyArgs([nestedObj], { depth: null }), - "{ a: { b: { c: { d: [Object] } } } }" - ); - // test inspect is working the same way - assertEquals( - inspect(nestedObj, { depth: 4 }), - "{ a: { b: { c: { d: [Object] } } } }" - ); -}); - -test(function consoleTestWithCustomInspector(): void { - class A { - [customInspect](): string { - return "b"; - } - } - - assertEquals(stringify(new A()), "b"); -}); - -test(function consoleTestWithIntegerFormatSpecifier(): void { - assertEquals(stringify("%i"), "%i"); - assertEquals(stringify("%i", 42.0), "42"); - assertEquals(stringify("%i", 42), "42"); - assertEquals(stringify("%i", "42"), "42"); - assertEquals(stringify("%i", "42.0"), "42"); - assertEquals(stringify("%i", 1.5), "1"); - assertEquals(stringify("%i", -0.5), "0"); - assertEquals(stringify("%i", ""), "NaN"); - assertEquals(stringify("%i", Symbol()), "NaN"); - assertEquals(stringify("%i %d", 42, 43), "42 43"); - assertEquals(stringify("%d %i", 42), "42 %i"); - assertEquals(stringify("%d", 12345678901234567890123), "1"); - assertEquals( - stringify("%i", 12345678901234567890123n), - "12345678901234567890123n" - ); -}); - -test(function consoleTestWithFloatFormatSpecifier(): void { - assertEquals(stringify("%f"), "%f"); - assertEquals(stringify("%f", 42.0), "42"); - assertEquals(stringify("%f", 42), "42"); - assertEquals(stringify("%f", "42"), "42"); - assertEquals(stringify("%f", "42.0"), "42"); - assertEquals(stringify("%f", 1.5), "1.5"); - assertEquals(stringify("%f", -0.5), "-0.5"); - assertEquals(stringify("%f", Math.PI), "3.141592653589793"); - assertEquals(stringify("%f", ""), "NaN"); - assertEquals(stringify("%f", Symbol("foo")), "NaN"); - assertEquals(stringify("%f", 5n), "5"); - assertEquals(stringify("%f %f", 42, 43), "42 43"); - assertEquals(stringify("%f %f", 42), "42 %f"); -}); - -test(function consoleTestWithStringFormatSpecifier(): void { - assertEquals(stringify("%s"), "%s"); - assertEquals(stringify("%s", undefined), "undefined"); - assertEquals(stringify("%s", "foo"), "foo"); - assertEquals(stringify("%s", 42), "42"); - assertEquals(stringify("%s", "42"), "42"); - assertEquals(stringify("%s %s", 42, 43), "42 43"); - assertEquals(stringify("%s %s", 42), "42 %s"); - assertEquals(stringify("%s", Symbol("foo")), "Symbol(foo)"); -}); - -test(function consoleTestWithObjectFormatSpecifier(): void { - assertEquals(stringify("%o"), "%o"); - assertEquals(stringify("%o", 42), "42"); - assertEquals(stringify("%o", "foo"), "foo"); - assertEquals(stringify("o: %o, a: %O", {}, []), "o: {}, a: []"); - assertEquals(stringify("%o", { a: 42 }), "{ a: 42 }"); - assertEquals( - stringify("%o", { a: { b: { c: { d: new Set([1]) } } } }), - "{ a: { b: { c: { d: [Set] } } } }" - ); -}); - -test(function consoleTestWithVariousOrInvalidFormatSpecifier(): void { - assertEquals(stringify("%s:%s"), "%s:%s"); - assertEquals(stringify("%i:%i"), "%i:%i"); - assertEquals(stringify("%d:%d"), "%d:%d"); - assertEquals(stringify("%%s%s", "foo"), "%sfoo"); - assertEquals(stringify("%s:%s", undefined), "undefined:%s"); - assertEquals(stringify("%s:%s", "foo", "bar"), "foo:bar"); - assertEquals(stringify("%s:%s", "foo", "bar", "baz"), "foo:bar baz"); - assertEquals(stringify("%%%s%%", "hi"), "%hi%"); - assertEquals(stringify("%d:%d", 12), "12:%d"); - assertEquals(stringify("%i:%i", 12), "12:%i"); - assertEquals(stringify("%f:%f", 12), "12:%f"); - assertEquals(stringify("o: %o, a: %o", {}), "o: {}, a: %o"); - assertEquals(stringify("abc%", 1), "abc% 1"); -}); - -test(function consoleTestCallToStringOnLabel(): void { - const methods = ["count", "countReset", "time", "timeLog", "timeEnd"]; - - for (const method of methods) { - let hasCalled = false; - - console[method]({ - toString(): void { - hasCalled = true; - } - }); - - assertEquals(hasCalled, true); - } -}); - -test(function consoleTestError(): void { - class MyError extends Error { - constructor(errStr: string) { - super(errStr); - this.name = "MyError"; - } - } - try { - throw new MyError("This is an error"); - } catch (e) { - assert( - stringify(e) - .split("\n")[0] // error has been caught - .includes("MyError: This is an error") - ); - } -}); - -test(function consoleTestClear(): void { - const stdoutWrite = stdout.write; - const uint8 = new TextEncoder().encode("\x1b[1;1H" + "\x1b[0J"); - let buffer = new Uint8Array(0); - - stdout.write = async (u8: Uint8Array): Promise<number> => { - const tmp = new Uint8Array(buffer.length + u8.length); - tmp.set(buffer, 0); - tmp.set(u8, buffer.length); - buffer = tmp; - - return await write(stdout.rid, u8); - }; - console.clear(); - stdout.write = stdoutWrite; - assertEquals(buffer, uint8); -}); - -// Test bound this issue -test(function consoleDetachedLog(): void { - const log = console.log; - const dir = console.dir; - const dirxml = console.dirxml; - const debug = console.debug; - const info = console.info; - const warn = console.warn; - const error = console.error; - const consoleAssert = console.assert; - const consoleCount = console.count; - const consoleCountReset = console.countReset; - const consoleTable = console.table; - const consoleTime = console.time; - const consoleTimeLog = console.timeLog; - const consoleTimeEnd = console.timeEnd; - const consoleGroup = console.group; - const consoleGroupEnd = console.groupEnd; - const consoleClear = console.clear; - log("Hello world"); - dir("Hello world"); - dirxml("Hello world"); - debug("Hello world"); - info("Hello world"); - warn("Hello world"); - error("Hello world"); - consoleAssert(true); - consoleCount("Hello world"); - consoleCountReset("Hello world"); - consoleTable({ test: "Hello world" }); - consoleTime("Hello world"); - consoleTimeLog("Hello world"); - consoleTimeEnd("Hello world"); - consoleGroup("Hello world"); - consoleGroupEnd(); - consoleClear(); -}); - -class StringBuffer { - chunks: string[] = []; - add(x: string): void { - this.chunks.push(x); - } - toString(): string { - return this.chunks.join(""); - } -} - -type ConsoleExamineFunc = ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - csl: any, - out: StringBuffer, - err?: StringBuffer, - both?: StringBuffer -) => void; - -function mockConsole(f: ConsoleExamineFunc): void { - const out = new StringBuffer(); - const err = new StringBuffer(); - const both = new StringBuffer(); - const csl = new Console( - (x: string, isErr: boolean, printsNewLine: boolean): void => { - const content = x + (printsNewLine ? "\n" : ""); - const buf = isErr ? err : out; - buf.add(content); - both.add(content); - } - ); - f(csl, out, err, both); -} - -// console.group test -test(function consoleGroup(): void { - mockConsole( - (console, out): void => { - console.group("1"); - console.log("2"); - console.group("3"); - console.log("4"); - console.groupEnd(); - console.groupEnd(); - console.log("5"); - console.log("6"); - - assertEquals( - out.toString(), - `1 - 2 - 3 - 4 -5 -6 -` - ); - } - ); -}); - -// console.group with console.warn test -test(function consoleGroupWarn(): void { - mockConsole( - (console, _out, _err, both): void => { - console.warn("1"); - console.group(); - console.warn("2"); - console.group(); - console.warn("3"); - console.groupEnd(); - console.warn("4"); - console.groupEnd(); - console.warn("5"); - - console.warn("6"); - console.warn("7"); - assertEquals( - both.toString(), - `1 - 2 - 3 - 4 -5 -6 -7 -` - ); - } - ); -}); - -// console.table test -test(function consoleTable(): void { - mockConsole( - (console, out): void => { - console.table({ a: "test", b: 1 }); - assertEquals( - out.toString(), - `┌─────────┬────────┐ -│ (index) │ Values │ -├─────────┼────────┤ -│ a │ "test" │ -│ b │ 1 │ -└─────────┴────────┘ -` - ); - } - ); - mockConsole( - (console, out): void => { - console.table({ a: { b: 10 }, b: { b: 20, c: 30 } }, ["c"]); - assertEquals( - out.toString(), - `┌─────────┬────┐ -│ (index) │ c │ -├─────────┼────┤ -│ a │ │ -│ b │ 30 │ -└─────────┴────┘ -` - ); - } - ); - mockConsole( - (console, out): void => { - console.table([1, 2, [3, [4]], [5, 6], [[7], [8]]]); - assertEquals( - out.toString(), - `┌─────────┬───────┬───────┬────────┐ -│ (index) │ 0 │ 1 │ Values │ -├─────────┼───────┼───────┼────────┤ -│ 0 │ │ │ 1 │ -│ 1 │ │ │ 2 │ -│ 2 │ 3 │ [ 4 ] │ │ -│ 3 │ 5 │ 6 │ │ -│ 4 │ [ 7 ] │ [ 8 ] │ │ -└─────────┴───────┴───────┴────────┘ -` - ); - } - ); - mockConsole( - (console, out): void => { - console.table(new Set([1, 2, 3, "test"])); - assertEquals( - out.toString(), - `┌───────────────────┬────────┐ -│ (iteration index) │ Values │ -├───────────────────┼────────┤ -│ 0 │ 1 │ -│ 1 │ 2 │ -│ 2 │ 3 │ -│ 3 │ "test" │ -└───────────────────┴────────┘ -` - ); - } - ); - mockConsole( - (console, out): void => { - console.table(new Map([[1, "one"], [2, "two"]])); - assertEquals( - out.toString(), - `┌───────────────────┬─────┬────────┐ -│ (iteration index) │ Key │ Values │ -├───────────────────┼─────┼────────┤ -│ 0 │ 1 │ "one" │ -│ 1 │ 2 │ "two" │ -└───────────────────┴─────┴────────┘ -` - ); - } - ); - mockConsole( - (console, out): void => { - console.table({ - a: true, - b: { c: { d: 10 }, e: [1, 2, [5, 6]] }, - f: "test", - g: new Set([1, 2, 3, "test"]), - h: new Map([[1, "one"]]) - }); - assertEquals( - out.toString(), - `┌─────────┬───────────┬───────────────────┬────────┐ -│ (index) │ c │ e │ Values │ -├─────────┼───────────┼───────────────────┼────────┤ -│ a │ │ │ true │ -│ b │ { d: 10 } │ [ 1, 2, [Array] ] │ │ -│ f │ │ │ "test" │ -│ g │ │ │ │ -│ h │ │ │ │ -└─────────┴───────────┴───────────────────┴────────┘ -` - ); - } - ); - mockConsole( - (console, out): void => { - console.table([ - 1, - "test", - false, - { a: 10 }, - ["test", { b: 20, c: "test" }] - ]); - assertEquals( - out.toString(), - `┌─────────┬────────┬──────────────────────┬────┬────────┐ -│ (index) │ 0 │ 1 │ a │ Values │ -├─────────┼────────┼──────────────────────┼────┼────────┤ -│ 0 │ │ │ │ 1 │ -│ 1 │ │ │ │ "test" │ -│ 2 │ │ │ │ false │ -│ 3 │ │ │ 10 │ │ -│ 4 │ "test" │ { b: 20, c: "test" } │ │ │ -└─────────┴────────┴──────────────────────┴────┴────────┘ -` - ); - } - ); - mockConsole( - (console, out): void => { - console.table([]); - assertEquals( - out.toString(), - `┌─────────┐ -│ (index) │ -├─────────┤ -└─────────┘ -` - ); - } - ); - mockConsole( - (console, out): void => { - console.table({}); - assertEquals( - out.toString(), - `┌─────────┐ -│ (index) │ -├─────────┤ -└─────────┘ -` - ); - } - ); - mockConsole( - (console, out): void => { - console.table(new Set()); - assertEquals( - out.toString(), - `┌───────────────────┐ -│ (iteration index) │ -├───────────────────┤ -└───────────────────┘ -` - ); - } - ); - mockConsole( - (console, out): void => { - console.table(new Map()); - assertEquals( - out.toString(), - `┌───────────────────┐ -│ (iteration index) │ -├───────────────────┤ -└───────────────────┘ -` - ); - } - ); - mockConsole( - (console, out): void => { - console.table("test"); - assertEquals(out.toString(), "test\n"); - } - ); -}); - -// console.log(Error) test -test(function consoleLogShouldNotThrowError(): void { - let result = 0; - try { - console.log(new Error("foo")); - result = 1; - } catch (e) { - result = 2; - } - assertEquals(result, 1); - - // output errors to the console should not include "Uncaught" - mockConsole( - (console, out): void => { - console.log(new Error("foo")); - assertEquals(out.toString().includes("Uncaught"), false); - } - ); -}); - -// console.dir test -test(function consoleDir(): void { - mockConsole( - (console, out): void => { - console.dir("DIR"); - assertEquals(out.toString(), "DIR\n"); - } - ); - mockConsole( - (console, out): void => { - console.dir("DIR", { indentLevel: 2 }); - assertEquals(out.toString(), " DIR\n"); - } - ); -}); - -// console.dir test -test(function consoleDirXml(): void { - mockConsole( - (console, out): void => { - console.dirxml("DIRXML"); - assertEquals(out.toString(), "DIRXML\n"); - } - ); - mockConsole( - (console, out): void => { - console.dirxml("DIRXML", { indentLevel: 2 }); - assertEquals(out.toString(), " DIRXML\n"); - } - ); -}); - -// console.trace test -test(function consoleTrace(): void { - mockConsole( - (console, _out, err): void => { - console.trace("%s", "custom message"); - assert(err.toString().includes("Trace: custom message")); - } - ); -}); diff --git a/js/copy_file.ts b/js/copy_file.ts deleted file mode 100644 index 94d2b63db..000000000 --- a/js/copy_file.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendSync, sendAsync } from "./dispatch_json.ts"; -import * as dispatch from "./dispatch.ts"; - -/** Copies the contents of a file to another by name synchronously. - * Creates a new file if target does not exists, and if target exists, - * overwrites original content of the target file. - * - * It would also copy the permission of the original file - * to the destination. - * - * Deno.copyFileSync("from.txt", "to.txt"); - */ -export function copyFileSync(from: string, to: string): void { - sendSync(dispatch.OP_COPY_FILE, { from, to }); -} - -/** Copies the contents of a file to another by name. - * - * Creates a new file if target does not exists, and if target exists, - * overwrites original content of the target file. - * - * It would also copy the permission of the original file - * to the destination. - * - * await Deno.copyFile("from.txt", "to.txt"); - */ -export async function copyFile(from: string, to: string): Promise<void> { - await sendAsync(dispatch.OP_COPY_FILE, { from, to }); -} diff --git a/js/copy_file_test.ts b/js/copy_file_test.ts deleted file mode 100644 index 72ae43f3e..000000000 --- a/js/copy_file_test.ts +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { testPerm, assert, assertEquals } from "./test_util.ts"; - -function readFileString(filename: string): string { - const dataRead = Deno.readFileSync(filename); - const dec = new TextDecoder("utf-8"); - return dec.decode(dataRead); -} - -function writeFileString(filename: string, s: string): void { - const enc = new TextEncoder(); - const data = enc.encode(s); - Deno.writeFileSync(filename, data, { perm: 0o666 }); -} - -function assertSameContent(filename1: string, filename2: string): void { - const data1 = Deno.readFileSync(filename1); - const data2 = Deno.readFileSync(filename2); - assertEquals(data1, data2); -} - -testPerm({ read: true, write: true }, function copyFileSyncSuccess(): void { - const tempDir = Deno.makeTempDirSync(); - const fromFilename = tempDir + "/from.txt"; - const toFilename = tempDir + "/to.txt"; - writeFileString(fromFilename, "Hello world!"); - Deno.copyFileSync(fromFilename, toFilename); - // No change to original file - assertEquals(readFileString(fromFilename), "Hello world!"); - // Original == Dest - assertSameContent(fromFilename, toFilename); -}); - -testPerm({ write: true, read: true }, function copyFileSyncFailure(): void { - const tempDir = Deno.makeTempDirSync(); - const fromFilename = tempDir + "/from.txt"; - const toFilename = tempDir + "/to.txt"; - // We skip initial writing here, from.txt does not exist - let err; - try { - Deno.copyFileSync(fromFilename, toFilename); - } catch (e) { - err = e; - } - assert(!!err); - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); - -testPerm({ write: true, read: false }, function copyFileSyncPerm1(): void { - let caughtError = false; - try { - Deno.copyFileSync("/from.txt", "/to.txt"); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); - -testPerm({ write: false, read: true }, function copyFileSyncPerm2(): void { - let caughtError = false; - try { - Deno.copyFileSync("/from.txt", "/to.txt"); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); - -testPerm({ read: true, write: true }, function copyFileSyncOverwrite(): void { - const tempDir = Deno.makeTempDirSync(); - const fromFilename = tempDir + "/from.txt"; - const toFilename = tempDir + "/to.txt"; - writeFileString(fromFilename, "Hello world!"); - // Make Dest exist and have different content - writeFileString(toFilename, "Goodbye!"); - Deno.copyFileSync(fromFilename, toFilename); - // No change to original file - assertEquals(readFileString(fromFilename), "Hello world!"); - // Original == Dest - assertSameContent(fromFilename, toFilename); -}); - -testPerm({ read: true, write: true }, async function copyFileSuccess(): Promise< - void -> { - const tempDir = Deno.makeTempDirSync(); - const fromFilename = tempDir + "/from.txt"; - const toFilename = tempDir + "/to.txt"; - writeFileString(fromFilename, "Hello world!"); - await Deno.copyFile(fromFilename, toFilename); - // No change to original file - assertEquals(readFileString(fromFilename), "Hello world!"); - // Original == Dest - assertSameContent(fromFilename, toFilename); -}); - -testPerm({ read: true, write: true }, async function copyFileFailure(): Promise< - void -> { - const tempDir = Deno.makeTempDirSync(); - const fromFilename = tempDir + "/from.txt"; - const toFilename = tempDir + "/to.txt"; - // We skip initial writing here, from.txt does not exist - let err; - try { - await Deno.copyFile(fromFilename, toFilename); - } catch (e) { - err = e; - } - assert(!!err); - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); - -testPerm( - { read: true, write: true }, - async function copyFileOverwrite(): Promise<void> { - const tempDir = Deno.makeTempDirSync(); - const fromFilename = tempDir + "/from.txt"; - const toFilename = tempDir + "/to.txt"; - writeFileString(fromFilename, "Hello world!"); - // Make Dest exist and have different content - writeFileString(toFilename, "Goodbye!"); - await Deno.copyFile(fromFilename, toFilename); - // No change to original file - assertEquals(readFileString(fromFilename), "Hello world!"); - // Original == Dest - assertSameContent(fromFilename, toFilename); - } -); - -testPerm({ read: false, write: true }, async function copyFilePerm1(): Promise< - void -> { - let caughtError = false; - try { - await Deno.copyFile("/from.txt", "/to.txt"); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); - -testPerm({ read: true, write: false }, async function copyFilePerm2(): Promise< - void -> { - let caughtError = false; - try { - await Deno.copyFile("/from.txt", "/to.txt"); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); diff --git a/js/core.ts b/js/core.ts deleted file mode 100644 index d394d822f..000000000 --- a/js/core.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { window } from "./window.ts"; - -// This allows us to access core in API even if we -// dispose window.Deno -export const core = window.Deno.core as DenoCore; diff --git a/js/custom_event.ts b/js/custom_event.ts deleted file mode 100644 index 922abd4b1..000000000 --- a/js/custom_event.ts +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2018 the Deno authors. All rights reserved. MIT license. -import * as domTypes from "./dom_types.ts"; -import * as event from "./event.ts"; -import { getPrivateValue, requiredArguments } from "./util.ts"; - -// WeakMaps are recommended for private attributes (see MDN link below) -// https://developer.mozilla.org/en-US/docs/Archive/Add-ons/Add-on_SDK/Guides/Contributor_s_Guide/Private_Properties#Using_WeakMaps -export const customEventAttributes = new WeakMap(); - -export class CustomEvent extends event.Event implements domTypes.CustomEvent { - constructor( - type: string, - customEventInitDict: domTypes.CustomEventInit = {} - ) { - requiredArguments("CustomEvent", arguments.length, 1); - super(type, customEventInitDict); - const { detail = null } = customEventInitDict; - customEventAttributes.set(this, { detail }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - get detail(): any { - return getPrivateValue(this, customEventAttributes, "detail"); - } - - initCustomEvent( - type: string, - bubbles?: boolean, - cancelable?: boolean, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - detail?: any - ): void { - if (this.dispatched) { - return; - } - - customEventAttributes.set(this, { detail }); - } - - get [Symbol.toStringTag](): string { - return "CustomEvent"; - } -} - -/** Built-in objects providing `get` methods for our - * interceptable JavaScript operations. - */ -Reflect.defineProperty(CustomEvent.prototype, "detail", { enumerable: true }); diff --git a/js/custom_event_test.ts b/js/custom_event_test.ts deleted file mode 100644 index 4d2eb2c16..000000000 --- a/js/custom_event_test.ts +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2018 the Deno authors. All rights reserved. MIT license. -import { test, assertEquals } from "./test_util.ts"; - -test(function customEventInitializedWithDetail(): void { - const type = "touchstart"; - const detail = { message: "hello" }; - const customEventInit = { - bubbles: true, - cancelable: true, - detail - } as CustomEventInit; - const event = new CustomEvent(type, customEventInit); - - assertEquals(event.bubbles, true); - assertEquals(event.cancelable, true); - assertEquals(event.currentTarget, null); - assertEquals(event.detail, detail); - assertEquals(event.isTrusted, false); - assertEquals(event.target, null); - assertEquals(event.type, type); -}); - -test(function toStringShouldBeWebCompatibility(): void { - const type = "touchstart"; - const event = new CustomEvent(type, {}); - assertEquals(event.toString(), "[object CustomEvent]"); -}); diff --git a/js/deno.ts b/js/deno.ts deleted file mode 100644 index 511e4f0ec..000000000 --- a/js/deno.ts +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. - -// Public deno module. -export { env, exit, isTTY, execPath, homeDir, hostname } from "./os.ts"; -export { chdir, cwd } from "./dir.ts"; -export { - File, - open, - openSync, - stdin, - stdout, - stderr, - read, - readSync, - write, - writeSync, - seek, - seekSync, - close, - OpenMode -} from "./files.ts"; -export { - EOF, - copy, - toAsyncIterator, - SeekMode, - Reader, - SyncReader, - Writer, - SyncWriter, - Closer, - Seeker, - SyncSeeker, - ReadCloser, - WriteCloser, - ReadSeeker, - WriteSeeker, - ReadWriteCloser, - ReadWriteSeeker -} from "./io.ts"; -export { - Buffer, - readAll, - readAllSync, - writeAll, - writeAllSync -} from "./buffer.ts"; -export { mkdirSync, mkdir } from "./mkdir.ts"; -export { - makeTempDirSync, - makeTempDir, - MakeTempDirOptions -} from "./make_temp_dir.ts"; -export { chmodSync, chmod } from "./chmod.ts"; -export { chownSync, chown } from "./chown.ts"; -export { utimeSync, utime } from "./utime.ts"; -export { removeSync, remove, RemoveOption } from "./remove.ts"; -export { renameSync, rename } from "./rename.ts"; -export { readFileSync, readFile } from "./read_file.ts"; -export { readDirSync, readDir } from "./read_dir.ts"; -export { copyFileSync, copyFile } from "./copy_file.ts"; -export { readlinkSync, readlink } from "./read_link.ts"; -export { statSync, lstatSync, stat, lstat } from "./stat.ts"; -export { linkSync, link } from "./link.ts"; -export { symlinkSync, symlink } from "./symlink.ts"; -export { writeFileSync, writeFile, WriteFileOptions } from "./write_file.ts"; -export { applySourceMap } from "./error_stack.ts"; -export { ErrorKind, DenoError } from "./errors.ts"; -export { - permissions, - revokePermission, - Permission, - Permissions -} from "./permissions.ts"; -export { truncateSync, truncate } from "./truncate.ts"; -export { FileInfo } from "./file_info.ts"; -export { connect, dial, listen, Listener, Conn } from "./net.ts"; -export { dialTLS } from "./tls.ts"; -export { metrics, Metrics } from "./metrics.ts"; -export { resources } from "./resources.ts"; -export { - kill, - run, - RunOptions, - Process, - ProcessStatus, - Signal -} from "./process.ts"; -export { inspect, customInspect } from "./console.ts"; -export { build, OperatingSystem, Arch } from "./build.ts"; -export { version } from "./version.ts"; -export const args: string[] = []; - -// These are internal Deno APIs. We are marking them as internal so they do not -// appear in the runtime type library. -/** @internal */ -export { core } from "./core.ts"; - -/** @internal */ -export { setPrepareStackTrace } from "./error_stack.ts"; - -// TODO Don't expose Console nor stringifyArgs. -/** @internal */ -export { Console, stringifyArgs } from "./console.ts"; -// TODO Don't expose DomIterableMixin. -/** @internal */ -export { DomIterableMixin } from "./mixins/dom_iterable.ts"; - -/** The current process id of the runtime. */ -export let pid: number; - -/** Reflects the NO_COLOR environment variable: https://no-color.org/ */ -export let noColor: boolean; - -// TODO(ry) This should not be exposed to Deno. -export function _setGlobals(pid_: number, noColor_: boolean): void { - pid = pid_; - noColor = noColor_; -} diff --git a/js/diagnostics.ts b/js/diagnostics.ts deleted file mode 100644 index 7cdb154b9..000000000 --- a/js/diagnostics.ts +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. - -// Diagnostic provides an abstraction for advice/errors received from a -// compiler, which is strongly influenced by the format of TypeScript -// diagnostics. - -/** The log category for a diagnostic message */ -export enum DiagnosticCategory { - Log = 0, - Debug = 1, - Info = 2, - Error = 3, - Warning = 4, - Suggestion = 5 -} - -export interface DiagnosticMessageChain { - message: string; - category: DiagnosticCategory; - code: number; - next?: DiagnosticMessageChain[]; -} - -export interface DiagnosticItem { - /** A string message summarizing the diagnostic. */ - message: string; - - /** An ordered array of further diagnostics. */ - messageChain?: DiagnosticMessageChain; - - /** Information related to the diagnostic. This is present when there is a - * suggestion or other additional diagnostic information */ - relatedInformation?: DiagnosticItem[]; - - /** The text of the source line related to the diagnostic */ - sourceLine?: string; - - /** The line number that is related to the diagnostic */ - lineNumber?: number; - - /** The name of the script resource related to the diagnostic */ - scriptResourceName?: string; - - /** The start position related to the diagnostic */ - startPosition?: number; - - /** The end position related to the diagnostic */ - endPosition?: number; - - /** The category of the diagnostic */ - category: DiagnosticCategory; - - /** A number identifier */ - code: number; - - /** The the start column of the sourceLine related to the diagnostic */ - startColumn?: number; - - /** The end column of the sourceLine related to the diagnostic */ - endColumn?: number; -} - -export interface Diagnostic { - /** An array of diagnostic items. */ - items: DiagnosticItem[]; -} - -interface SourceInformation { - sourceLine: string; - lineNumber: number; - scriptResourceName: string; - startColumn: number; - endColumn: number; -} - -function fromDiagnosticCategory( - category: ts.DiagnosticCategory -): DiagnosticCategory { - switch (category) { - case ts.DiagnosticCategory.Error: - return DiagnosticCategory.Error; - case ts.DiagnosticCategory.Message: - return DiagnosticCategory.Info; - case ts.DiagnosticCategory.Suggestion: - return DiagnosticCategory.Suggestion; - case ts.DiagnosticCategory.Warning: - return DiagnosticCategory.Warning; - default: - throw new Error( - `Unexpected DiagnosticCategory: "${category}"/"${ - ts.DiagnosticCategory[category] - }"` - ); - } -} - -function getSourceInformation( - sourceFile: ts.SourceFile, - start: number, - length: number -): SourceInformation { - const scriptResourceName = sourceFile.fileName; - const { - line: lineNumber, - character: startColumn - } = sourceFile.getLineAndCharacterOfPosition(start); - const endPosition = sourceFile.getLineAndCharacterOfPosition(start + length); - const endColumn = - lineNumber === endPosition.line ? endPosition.character : startColumn; - const lastLineInFile = sourceFile.getLineAndCharacterOfPosition( - sourceFile.text.length - ).line; - const lineStart = sourceFile.getPositionOfLineAndCharacter(lineNumber, 0); - const lineEnd = - lineNumber < lastLineInFile - ? sourceFile.getPositionOfLineAndCharacter(lineNumber + 1, 0) - : sourceFile.text.length; - const sourceLine = sourceFile.text - .slice(lineStart, lineEnd) - .replace(/\s+$/g, "") - .replace("\t", " "); - return { - sourceLine, - lineNumber, - scriptResourceName, - startColumn, - endColumn - }; -} - -/** Converts a TypeScript diagnostic message chain to a Deno one. */ -function fromDiagnosticMessageChain( - messageChain: ts.DiagnosticMessageChain[] | undefined -): DiagnosticMessageChain[] | undefined { - if (!messageChain) { - return undefined; - } - - return messageChain.map(({ messageText: message, code, category, next }) => { - return { - message, - code, - category: fromDiagnosticCategory(category), - next: fromDiagnosticMessageChain(next) - }; - }); -} - -/** Parse out information from a TypeScript diagnostic structure. */ -function parseDiagnostic( - item: ts.Diagnostic | ts.DiagnosticRelatedInformation -): DiagnosticItem { - const { - messageText, - category: sourceCategory, - code, - file, - start: startPosition, - length - } = item; - const sourceInfo = - file && startPosition && length - ? getSourceInformation(file, startPosition, length) - : undefined; - const endPosition = - startPosition && length ? startPosition + length : undefined; - const category = fromDiagnosticCategory(sourceCategory); - - let message: string; - let messageChain: DiagnosticMessageChain | undefined; - if (typeof messageText === "string") { - message = messageText; - } else { - message = messageText.messageText; - messageChain = fromDiagnosticMessageChain([messageText])![0]; - } - - const base = { - message, - messageChain, - code, - category, - startPosition, - endPosition - }; - - return sourceInfo ? { ...base, ...sourceInfo } : base; -} - -/** Convert a diagnostic related information array into a Deno diagnostic - * array. */ -function parseRelatedInformation( - relatedInformation: readonly ts.DiagnosticRelatedInformation[] -): DiagnosticItem[] { - const result: DiagnosticItem[] = []; - for (const item of relatedInformation) { - result.push(parseDiagnostic(item)); - } - return result; -} - -/** Convert TypeScript diagnostics to Deno diagnostics. */ -export function fromTypeScriptDiagnostic( - diagnostics: readonly ts.Diagnostic[] -): Diagnostic { - const items: DiagnosticItem[] = []; - for (const sourceDiagnostic of diagnostics) { - const item: DiagnosticItem = parseDiagnostic(sourceDiagnostic); - if (sourceDiagnostic.relatedInformation) { - item.relatedInformation = parseRelatedInformation( - sourceDiagnostic.relatedInformation - ); - } - items.push(item); - } - return { items }; -} diff --git a/js/dir.ts b/js/dir.ts deleted file mode 100644 index ef1111555..000000000 --- a/js/dir.ts +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendSync } from "./dispatch_json.ts"; -import * as dispatch from "./dispatch.ts"; - -/** - * `cwd()` Return a string representing the current working directory. - * If the current directory can be reached via multiple paths - * (due to symbolic links), `cwd()` may return - * any one of them. - * throws `NotFound` exception if directory not available - */ -export function cwd(): string { - return sendSync(dispatch.OP_CWD); -} - -/** - * `chdir()` Change the current working directory to path. - * throws `NotFound` exception if directory not available - */ -export function chdir(directory: string): void { - sendSync(dispatch.OP_CHDIR, { directory }); -} diff --git a/js/dir_test.ts b/js/dir_test.ts deleted file mode 100644 index 6c4e36d7a..000000000 --- a/js/dir_test.ts +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, testPerm, assert, assertEquals } from "./test_util.ts"; - -test(function dirCwdNotNull(): void { - assert(Deno.cwd() != null); -}); - -testPerm({ write: true }, function dirCwdChdirSuccess(): void { - const initialdir = Deno.cwd(); - const path = Deno.makeTempDirSync(); - Deno.chdir(path); - const current = Deno.cwd(); - if (Deno.build.os === "mac") { - assertEquals(current, "/private" + path); - } else { - assertEquals(current, path); - } - Deno.chdir(initialdir); -}); - -testPerm({ write: true }, function dirCwdError(): void { - // excluding windows since it throws resource busy, while removeSync - if (["linux", "mac"].includes(Deno.build.os)) { - const initialdir = Deno.cwd(); - const path = Deno.makeTempDirSync(); - Deno.chdir(path); - Deno.removeSync(path); - try { - Deno.cwd(); - throw Error("current directory removed, should throw error"); - } catch (err) { - if (err instanceof Deno.DenoError) { - console.log(err.name === "NotFound"); - } else { - throw Error("raised different exception"); - } - } - Deno.chdir(initialdir); - } -}); - -testPerm({ write: true }, function dirChdirError(): void { - const path = Deno.makeTempDirSync() + "test"; - try { - Deno.chdir(path); - throw Error("directory not available, should throw error"); - } catch (err) { - if (err instanceof Deno.DenoError) { - console.log(err.name === "NotFound"); - } else { - throw Error("raised different exception"); - } - } -}); diff --git a/js/dispatch.ts b/js/dispatch.ts deleted file mode 100644 index bff4d0f5b..000000000 --- a/js/dispatch.ts +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as minimal from "./dispatch_minimal.ts"; -import * as json from "./dispatch_json.ts"; - -// These consts are shared with Rust. Update with care. -export let OP_READ: number; -export let OP_WRITE: number; -export let OP_EXIT: number; -export let OP_IS_TTY: number; -export let OP_ENV: number; -export let OP_EXEC_PATH: number; -export let OP_UTIME: number; -export let OP_SET_ENV: number; -export let OP_GET_ENV: number; -export let OP_HOME_DIR: number; -export let OP_START: number; -export let OP_APPLY_SOURCE_MAP: number; -export let OP_FORMAT_ERROR: number; -export let OP_CACHE: number; -export let OP_FETCH_SOURCE_FILES: number; -export let OP_OPEN: number; -export let OP_CLOSE: number; -export let OP_SEEK: number; -export let OP_FETCH: number; -export let OP_METRICS: number; -export let OP_REPL_START: number; -export let OP_REPL_READLINE: number; -export let OP_ACCEPT: number; -export let OP_DIAL: number; -export let OP_SHUTDOWN: number; -export let OP_LISTEN: number; -export let OP_RESOURCES: number; -export let OP_GET_RANDOM_VALUES: number; -export let OP_GLOBAL_TIMER_STOP: number; -export let OP_GLOBAL_TIMER: number; -export let OP_NOW: number; -export let OP_PERMISSIONS: number; -export let OP_REVOKE_PERMISSION: number; -export let OP_CREATE_WORKER: number; -export let OP_HOST_GET_WORKER_CLOSED: number; -export let OP_HOST_POST_MESSAGE: number; -export let OP_HOST_GET_MESSAGE: number; -export let OP_WORKER_POST_MESSAGE: number; -export let OP_WORKER_GET_MESSAGE: number; -export let OP_RUN: number; -export let OP_RUN_STATUS: number; -export let OP_KILL: number; -export let OP_CHDIR: number; -export let OP_MKDIR: number; -export let OP_CHMOD: number; -export let OP_CHOWN: number; -export let OP_REMOVE: number; -export let OP_COPY_FILE: number; -export let OP_STAT: number; -export let OP_READ_DIR: number; -export let OP_RENAME: number; -export let OP_LINK: number; -export let OP_SYMLINK: number; -export let OP_READ_LINK: number; -export let OP_TRUNCATE: number; -export let OP_MAKE_TEMP_DIR: number; -export let OP_CWD: number; -export let OP_FETCH_ASSET: number; -export let OP_DIAL_TLS: number; -export let OP_HOSTNAME: number; - -export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void { - switch (opId) { - case OP_WRITE: - case OP_READ: - minimal.asyncMsgFromRust(opId, ui8); - break; - case OP_EXIT: - case OP_IS_TTY: - case OP_ENV: - case OP_EXEC_PATH: - case OP_UTIME: - case OP_OPEN: - case OP_SEEK: - case OP_FETCH: - case OP_REPL_START: - case OP_REPL_READLINE: - case OP_ACCEPT: - case OP_DIAL: - case OP_GLOBAL_TIMER: - case OP_HOST_GET_WORKER_CLOSED: - case OP_HOST_GET_MESSAGE: - case OP_WORKER_GET_MESSAGE: - case OP_RUN_STATUS: - case OP_MKDIR: - case OP_CHMOD: - case OP_CHOWN: - case OP_REMOVE: - case OP_COPY_FILE: - case OP_STAT: - case OP_READ_DIR: - case OP_RENAME: - case OP_LINK: - case OP_SYMLINK: - case OP_READ_LINK: - case OP_TRUNCATE: - case OP_MAKE_TEMP_DIR: - case OP_DIAL_TLS: - case OP_FETCH_SOURCE_FILES: - json.asyncMsgFromRust(opId, ui8); - break; - default: - throw Error("bad async opId"); - } -} diff --git a/js/dispatch_json.ts b/js/dispatch_json.ts deleted file mode 100644 index 572ec855a..000000000 --- a/js/dispatch_json.ts +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as util from "./util.ts"; -import { TextEncoder, TextDecoder } from "./text_encoding.ts"; -import { core } from "./core.ts"; -import { ErrorKind, DenoError } from "./errors.ts"; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type Ok = any; - -interface JsonError { - kind: ErrorKind; - message: string; -} - -interface JsonResponse { - ok?: Ok; - err?: JsonError; - promiseId?: number; // Only present in async messages. -} - -const promiseTable = new Map<number, util.Resolvable<JsonResponse>>(); -let _nextPromiseId = 1; - -function nextPromiseId(): number { - return _nextPromiseId++; -} - -function decode(ui8: Uint8Array): JsonResponse { - const s = new TextDecoder().decode(ui8); - return JSON.parse(s) as JsonResponse; -} - -function encode(args: object): Uint8Array { - const s = JSON.stringify(args); - return new TextEncoder().encode(s); -} - -function unwrapResponse(res: JsonResponse): Ok { - if (res.err != null) { - throw new DenoError(res.err!.kind, res.err!.message); - } - util.assert(res.ok != null); - return res.ok!; -} - -export function asyncMsgFromRust(opId: number, resUi8: Uint8Array): void { - const res = decode(resUi8); - util.assert(res.promiseId != null); - - const promise = promiseTable.get(res.promiseId!); - util.assert(promise != null); - promiseTable.delete(res.promiseId!); - promise!.resolve(res); -} - -export function sendSync( - opId: number, - args: object = {}, - zeroCopy?: Uint8Array -): Ok { - const argsUi8 = encode(args); - const resUi8 = core.dispatch(opId, argsUi8, zeroCopy); - util.assert(resUi8 != null); - - const res = decode(resUi8!); - util.assert(res.promiseId == null); - return unwrapResponse(res); -} - -export async function sendAsync( - opId: number, - args: object = {}, - zeroCopy?: Uint8Array -): Promise<Ok> { - const promiseId = nextPromiseId(); - args = Object.assign(args, { promiseId }); - const promise = util.createResolvable<Ok>(); - promiseTable.set(promiseId, promise); - - const argsUi8 = encode(args); - const resUi8 = core.dispatch(opId, argsUi8, zeroCopy); - util.assert(resUi8 == null); - - const res = await promise; - return unwrapResponse(res); -} diff --git a/js/dispatch_json_test.ts b/js/dispatch_json_test.ts deleted file mode 100644 index 11dadc620..000000000 --- a/js/dispatch_json_test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { testPerm, assertMatch, unreachable } from "./test_util.ts"; - -const openErrorStackPattern = new RegExp( - `^.* - at unwrapResponse \\(.*dispatch_json\\.ts:.*\\) - at Object.sendAsync \\(.*dispatch_json\\.ts:.*\\) - at async Object\\.open \\(.*files\\.ts:.*\\).*$`, - "ms" -); - -testPerm({ read: true }, async function sendAsyncStackTrace(): Promise<void> { - await Deno.open("nonexistent.txt") - .then(unreachable) - .catch( - (error): void => { - assertMatch(error.stack, openErrorStackPattern); - } - ); -}); diff --git a/js/dispatch_minimal.ts b/js/dispatch_minimal.ts deleted file mode 100644 index 98636f85b..000000000 --- a/js/dispatch_minimal.ts +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as util from "./util.ts"; -import { core } from "./core.ts"; - -const promiseTableMin = new Map<number, util.Resolvable<number>>(); -// Note it's important that promiseId starts at 1 instead of 0, because sync -// messages are indicated with promiseId 0. If we ever add wrap around logic for -// overflows, this should be taken into account. -let _nextPromiseId = 1; - -function nextPromiseId(): number { - return _nextPromiseId++; -} - -export interface RecordMinimal { - promiseId: number; - opId: number; // Maybe better called dispatchId - arg: number; - result: number; -} - -export function recordFromBufMinimal( - opId: number, - buf32: Int32Array -): RecordMinimal { - if (buf32.length != 3) { - throw Error("Bad message"); - } - return { - promiseId: buf32[0], - opId, - arg: buf32[1], - result: buf32[2] - }; -} - -const scratch32 = new Int32Array(3); -const scratchBytes = new Uint8Array( - scratch32.buffer, - scratch32.byteOffset, - scratch32.byteLength -); -util.assert(scratchBytes.byteLength === scratch32.length * 4); - -export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void { - const buf32 = new Int32Array(ui8.buffer, ui8.byteOffset, ui8.byteLength / 4); - const record = recordFromBufMinimal(opId, buf32); - const { promiseId, result } = record; - const promise = promiseTableMin.get(promiseId); - promiseTableMin.delete(promiseId); - promise!.resolve(result); -} - -export function sendAsyncMinimal( - opId: number, - arg: number, - zeroCopy: Uint8Array -): Promise<number> { - const promiseId = nextPromiseId(); // AKA cmdId - scratch32[0] = promiseId; - scratch32[1] = arg; - scratch32[2] = 0; // result - const promise = util.createResolvable<number>(); - promiseTableMin.set(promiseId, promise); - core.dispatch(opId, scratchBytes, zeroCopy); - return promise; -} - -export function sendSyncMinimal( - opId: number, - arg: number, - zeroCopy: Uint8Array -): number { - scratch32[0] = 0; // promiseId 0 indicates sync - scratch32[1] = arg; - const res = core.dispatch(opId, scratchBytes, zeroCopy)!; - const res32 = new Int32Array(res.buffer, res.byteOffset, 3); - const resRecord = recordFromBufMinimal(opId, res32); - return resRecord.result; -} diff --git a/js/dom_file.ts b/js/dom_file.ts deleted file mode 100644 index 1f9bf93a5..000000000 --- a/js/dom_file.ts +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as domTypes from "./dom_types.ts"; -import * as blob from "./blob.ts"; - -export class DomFileImpl extends blob.DenoBlob implements domTypes.DomFile { - lastModified: number; - name: string; - - constructor( - fileBits: domTypes.BlobPart[], - fileName: string, - options?: domTypes.FilePropertyBag - ) { - options = options || {}; - super(fileBits, options); - - // 4.1.2.1 Replace any "/" character (U+002F SOLIDUS) - // with a ":" (U + 003A COLON) - this.name = String(fileName).replace(/\u002F/g, "\u003A"); - // 4.1.3.3 If lastModified is not provided, set lastModified to the current - // date and time represented in number of milliseconds since the Unix Epoch. - this.lastModified = options.lastModified || Date.now(); - } -} diff --git a/js/dom_types.ts b/js/dom_types.ts deleted file mode 100644 index 308505cf5..000000000 --- a/js/dom_types.ts +++ /dev/null @@ -1,625 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. - -/*! **************************************************************************** -Copyright (c) Microsoft Corporation. All rights reserved. -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at http://www.apache.org/licenses/LICENSE-2.0 - -THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF -ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED -WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, -MERCHANTABLITY OR NON-INFRINGEMENT. - -See the Apache Version 2.0 License for specific language governing permissions -and limitations under the License. -*******************************************************************************/ - -/* eslint-disable @typescript-eslint/no-explicit-any */ - -export type BufferSource = ArrayBufferView | ArrayBuffer; - -export type HeadersInit = - | Headers - | Array<[string, string]> - | Record<string, string>; -export type URLSearchParamsInit = string | string[][] | Record<string, string>; -type BodyInit = - | Blob - | BufferSource - | FormData - | URLSearchParams - | ReadableStream - | string; -export type RequestInfo = Request | string; -type ReferrerPolicy = - | "" - | "no-referrer" - | "no-referrer-when-downgrade" - | "origin-only" - | "origin-when-cross-origin" - | "unsafe-url"; -export type BlobPart = BufferSource | Blob | string; -export type FormDataEntryValue = DomFile | string; - -export interface DomIterable<K, V> { - keys(): IterableIterator<K>; - values(): IterableIterator<V>; - entries(): IterableIterator<[K, V]>; - [Symbol.iterator](): IterableIterator<[K, V]>; - forEach( - callback: (value: V, key: K, parent: this) => void, - thisArg?: any - ): void; -} - -type EndingType = "transparent" | "native"; - -export interface BlobPropertyBag { - type?: string; - ending?: EndingType; -} - -interface AbortSignalEventMap { - abort: ProgressEvent; -} - -// https://dom.spec.whatwg.org/#node -export enum NodeType { - ELEMENT_NODE = 1, - TEXT_NODE = 3, - DOCUMENT_FRAGMENT_NODE = 11 -} - -export const eventTargetHost: unique symbol = Symbol(); -export const eventTargetListeners: unique symbol = Symbol(); -export const eventTargetMode: unique symbol = Symbol(); -export const eventTargetNodeType: unique symbol = Symbol(); - -export interface EventTarget { - [eventTargetHost]: EventTarget | null; - [eventTargetListeners]: { [type in string]: EventListener[] }; - [eventTargetMode]: string; - [eventTargetNodeType]: NodeType; - addEventListener( - type: string, - callback: (event: Event) => void | null, - options?: boolean | AddEventListenerOptions - ): void; - dispatchEvent(event: Event): boolean; - removeEventListener( - type: string, - callback?: (event: Event) => void | null, - options?: EventListenerOptions | boolean - ): void; -} - -export interface ProgressEventInit extends EventInit { - lengthComputable?: boolean; - loaded?: number; - total?: number; -} - -export interface URLSearchParams extends DomIterable<string, string> { - /** - * Appends a specified key/value pair as a new search parameter. - */ - append(name: string, value: string): void; - /** - * Deletes the given search parameter, and its associated value, - * from the list of all search parameters. - */ - delete(name: string): void; - /** - * Returns the first value associated to the given search parameter. - */ - get(name: string): string | null; - /** - * Returns all the values association with a given search parameter. - */ - getAll(name: string): string[]; - /** - * Returns a Boolean indicating if such a search parameter exists. - */ - has(name: string): boolean; - /** - * Sets the value associated to a given search parameter to the given value. - * If there were several values, delete the others. - */ - set(name: string, value: string): void; - /** - * Sort all key/value pairs contained in this object in place - * and return undefined. The sort order is according to Unicode - * code points of the keys. - */ - sort(): void; - /** - * Returns a query string suitable for use in a URL. - */ - toString(): string; - /** - * Iterates over each name-value pair in the query - * and invokes the given function. - */ - forEach( - callbackfn: (value: string, key: string, parent: this) => void, - thisArg?: any - ): void; -} - -export interface EventListener { - handleEvent(event: Event): void; - readonly callback: (event: Event) => void | null; - readonly options: boolean | AddEventListenerOptions; -} - -export interface EventInit { - bubbles?: boolean; - cancelable?: boolean; - composed?: boolean; -} - -export interface CustomEventInit extends EventInit { - detail?: any; -} - -export enum EventPhase { - NONE = 0, - CAPTURING_PHASE = 1, - AT_TARGET = 2, - BUBBLING_PHASE = 3 -} - -export interface EventPath { - item: EventTarget; - itemInShadowTree: boolean; - relatedTarget: EventTarget | null; - rootOfClosedTree: boolean; - slotInClosedTree: boolean; - target: EventTarget | null; - touchTargetList: EventTarget[]; -} - -export interface Event { - readonly type: string; - target: EventTarget | null; - currentTarget: EventTarget | null; - composedPath(): EventPath[]; - - eventPhase: number; - - stopPropagation(): void; - stopImmediatePropagation(): void; - - readonly bubbles: boolean; - readonly cancelable: boolean; - preventDefault(): void; - readonly defaultPrevented: boolean; - readonly composed: boolean; - - isTrusted: boolean; - readonly timeStamp: Date; - - dispatched: boolean; - readonly initialized: boolean; - inPassiveListener: boolean; - cancelBubble: boolean; - cancelBubbleImmediately: boolean; - path: EventPath[]; - relatedTarget: EventTarget | null; -} - -export interface CustomEvent extends Event { - readonly detail: any; - initCustomEvent( - type: string, - bubbles?: boolean, - cancelable?: boolean, - detail?: any | null - ): void; -} - -export interface DomFile extends Blob { - readonly lastModified: number; - readonly name: string; -} - -export interface DomFileConstructor { - new (bits: BlobPart[], filename: string, options?: FilePropertyBag): DomFile; - prototype: DomFile; -} - -export interface FilePropertyBag extends BlobPropertyBag { - lastModified?: number; -} - -interface ProgressEvent extends Event { - readonly lengthComputable: boolean; - readonly loaded: number; - readonly total: number; -} - -export interface EventListenerOptions { - capture: boolean; -} - -export interface AddEventListenerOptions extends EventListenerOptions { - once: boolean; - passive: boolean; -} - -interface AbortSignal extends EventTarget { - readonly aborted: boolean; - onabort: ((this: AbortSignal, ev: ProgressEvent) => any) | null; - addEventListener<K extends keyof AbortSignalEventMap>( - type: K, - listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, - options?: boolean | AddEventListenerOptions - ): void; - addEventListener( - type: string, - listener: EventListener, - options?: boolean | AddEventListenerOptions - ): void; - removeEventListener<K extends keyof AbortSignalEventMap>( - type: K, - listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, - options?: boolean | EventListenerOptions - ): void; - removeEventListener( - type: string, - listener: EventListener, - options?: boolean | EventListenerOptions - ): void; -} - -export interface ReadableStream { - readonly locked: boolean; - cancel(): Promise<void>; - getReader(): ReadableStreamReader; - tee(): [ReadableStream, ReadableStream]; -} - -export interface ReadableStreamReader { - cancel(): Promise<void>; - read(): Promise<any>; - releaseLock(): void; -} - -export interface FormData extends DomIterable<string, FormDataEntryValue> { - append(name: string, value: string | Blob, fileName?: string): void; - delete(name: string): void; - get(name: string): FormDataEntryValue | null; - getAll(name: string): FormDataEntryValue[]; - has(name: string): boolean; - set(name: string, value: string | Blob, fileName?: string): void; -} - -export interface FormDataConstructor { - new (): FormData; - prototype: FormData; -} - -/** A blob object represents a file-like object of immutable, raw data. */ -export interface Blob { - /** The size, in bytes, of the data contained in the `Blob` object. */ - readonly size: number; - /** A string indicating the media type of the data contained in the `Blob`. - * If the type is unknown, this string is empty. - */ - readonly type: string; - /** Returns a new `Blob` object containing the data in the specified range of - * bytes of the source `Blob`. - */ - slice(start?: number, end?: number, contentType?: string): Blob; -} - -export interface Body { - /** A simple getter used to expose a `ReadableStream` of the body contents. */ - readonly body: ReadableStream | null; - /** Stores a `Boolean` that declares whether the body has been used in a - * response yet. - */ - readonly bodyUsed: boolean; - /** Takes a `Response` stream and reads it to completion. It returns a promise - * that resolves with an `ArrayBuffer`. - */ - arrayBuffer(): Promise<ArrayBuffer>; - /** Takes a `Response` stream and reads it to completion. It returns a promise - * that resolves with a `Blob`. - */ - blob(): Promise<Blob>; - /** Takes a `Response` stream and reads it to completion. It returns a promise - * that resolves with a `FormData` object. - */ - formData(): Promise<FormData>; - /** Takes a `Response` stream and reads it to completion. It returns a promise - * that resolves with the result of parsing the body text as JSON. - */ - json(): Promise<any>; - /** Takes a `Response` stream and reads it to completion. It returns a promise - * that resolves with a `USVString` (text). - */ - text(): Promise<string>; -} - -export interface Headers extends DomIterable<string, string> { - /** Appends a new value onto an existing header inside a `Headers` object, or - * adds the header if it does not already exist. - */ - append(name: string, value: string): void; - /** Deletes a header from a `Headers` object. */ - delete(name: string): void; - /** Returns an iterator allowing to go through all key/value pairs - * contained in this Headers object. The both the key and value of each pairs - * are ByteString objects. - */ - entries(): IterableIterator<[string, string]>; - /** Returns a `ByteString` sequence of all the values of a header within a - * `Headers` object with a given name. - */ - get(name: string): string | null; - /** Returns a boolean stating whether a `Headers` object contains a certain - * header. - */ - has(name: string): boolean; - /** Returns an iterator allowing to go through all keys contained in - * this Headers object. The keys are ByteString objects. - */ - keys(): IterableIterator<string>; - /** Sets a new value for an existing header inside a Headers object, or adds - * the header if it does not already exist. - */ - set(name: string, value: string): void; - /** Returns an iterator allowing to go through all values contained in - * this Headers object. The values are ByteString objects. - */ - values(): IterableIterator<string>; - forEach( - callbackfn: (value: string, key: string, parent: this) => void, - thisArg?: any - ): void; - /** The Symbol.iterator well-known symbol specifies the default - * iterator for this Headers object - */ - [Symbol.iterator](): IterableIterator<[string, string]>; -} - -export interface HeadersConstructor { - new (init?: HeadersInit): Headers; - prototype: Headers; -} - -type RequestCache = - | "default" - | "no-store" - | "reload" - | "no-cache" - | "force-cache" - | "only-if-cached"; -type RequestCredentials = "omit" | "same-origin" | "include"; -type RequestDestination = - | "" - | "audio" - | "audioworklet" - | "document" - | "embed" - | "font" - | "image" - | "manifest" - | "object" - | "paintworklet" - | "report" - | "script" - | "sharedworker" - | "style" - | "track" - | "video" - | "worker" - | "xslt"; -type RequestMode = "navigate" | "same-origin" | "no-cors" | "cors"; -type RequestRedirect = "follow" | "error" | "manual"; -type ResponseType = - | "basic" - | "cors" - | "default" - | "error" - | "opaque" - | "opaqueredirect"; - -export interface RequestInit { - body?: BodyInit | null; - cache?: RequestCache; - credentials?: RequestCredentials; - headers?: HeadersInit; - integrity?: string; - keepalive?: boolean; - method?: string; - mode?: RequestMode; - redirect?: RequestRedirect; - referrer?: string; - referrerPolicy?: ReferrerPolicy; - signal?: AbortSignal | null; - window?: any; -} - -export interface ResponseInit { - headers?: HeadersInit; - status?: number; - statusText?: string; -} - -export interface RequestConstructor { - new (input: RequestInfo, init?: RequestInit): Request; - prototype: Request; -} - -export interface Request extends Body { - /** Returns the cache mode associated with request, which is a string - * indicating how the the request will interact with the browser's cache when - * fetching. - */ - readonly cache?: RequestCache; - /** Returns the credentials mode associated with request, which is a string - * indicating whether credentials will be sent with the request always, never, - * or only when sent to a same-origin URL. - */ - readonly credentials?: RequestCredentials; - /** Returns the kind of resource requested by request, (e.g., `document` or - * `script`). - */ - readonly destination?: RequestDestination; - /** Returns a Headers object consisting of the headers associated with - * request. - * - * Note that headers added in the network layer by the user agent - * will not be accounted for in this object, (e.g., the `Host` header). - */ - readonly headers: Headers; - /** Returns request's subresource integrity metadata, which is a cryptographic - * hash of the resource being fetched. Its value consists of multiple hashes - * separated by whitespace. [SRI] - */ - readonly integrity?: string; - /** Returns a boolean indicating whether or not request is for a history - * navigation (a.k.a. back-forward navigation). - */ - readonly isHistoryNavigation?: boolean; - /** Returns a boolean indicating whether or not request is for a reload - * navigation. - */ - readonly isReloadNavigation?: boolean; - /** Returns a boolean indicating whether or not request can outlive the global - * in which it was created. - */ - readonly keepalive?: boolean; - /** Returns request's HTTP method, which is `GET` by default. */ - readonly method: string; - /** Returns the mode associated with request, which is a string indicating - * whether the request will use CORS, or will be restricted to same-origin - * URLs. - */ - readonly mode?: RequestMode; - /** Returns the redirect mode associated with request, which is a string - * indicating how redirects for the request will be handled during fetching. - * - * A request will follow redirects by default. - */ - readonly redirect?: RequestRedirect; - /** Returns the referrer of request. Its value can be a same-origin URL if - * explicitly set in init, the empty string to indicate no referrer, and - * `about:client` when defaulting to the global's default. - * - * This is used during fetching to determine the value of the `Referer` - * header of the request being made. - */ - readonly referrer?: string; - /** Returns the referrer policy associated with request. This is used during - * fetching to compute the value of the request's referrer. - */ - readonly referrerPolicy?: ReferrerPolicy; - /** Returns the signal associated with request, which is an AbortSignal object - * indicating whether or not request has been aborted, and its abort event - * handler. - */ - readonly signal?: AbortSignal; - /** Returns the URL of request as a string. */ - readonly url: string; - clone(): Request; -} - -export interface Response extends Body { - /** Contains the `Headers` object associated with the response. */ - readonly headers: Headers; - /** Contains a boolean stating whether the response was successful (status in - * the range 200-299) or not. - */ - readonly ok: boolean; - /** Indicates whether or not the response is the result of a redirect; that - * is, its URL list has more than one entry. - */ - readonly redirected: boolean; - /** Contains the status code of the response (e.g., `200` for a success). */ - readonly status: number; - /** Contains the status message corresponding to the status code (e.g., `OK` - * for `200`). - */ - readonly statusText: string; - readonly trailer: Promise<Headers>; - /** Contains the type of the response (e.g., `basic`, `cors`). */ - readonly type: ResponseType; - /** Contains the URL of the response. */ - readonly url: string; - /** Creates a clone of a `Response` object. */ - clone(): Response; -} - -export interface Location { - /** - * Returns a DOMStringList object listing the origins of the ancestor browsing - * contexts, from the parent browsing context to the top-level browsing - * context. - */ - readonly ancestorOrigins: string[]; - /** - * Returns the Location object's URL's fragment (includes leading "#" if - * non-empty). - * Can be set, to navigate to the same URL with a changed fragment (ignores - * leading "#"). - */ - hash: string; - /** - * Returns the Location object's URL's host and port (if different from the - * default port for the scheme). Can be set, to navigate to the same URL with - * a changed host and port. - */ - host: string; - /** - * Returns the Location object's URL's host. Can be set, to navigate to the - * same URL with a changed host. - */ - hostname: string; - /** - * Returns the Location object's URL. Can be set, to navigate to the given - * URL. - */ - href: string; - /** Returns the Location object's URL's origin. */ - readonly origin: string; - /** - * Returns the Location object's URL's path. - * Can be set, to navigate to the same URL with a changed path. - */ - pathname: string; - /** - * Returns the Location object's URL's port. - * Can be set, to navigate to the same URL with a changed port. - */ - port: string; - /** - * Returns the Location object's URL's scheme. - * Can be set, to navigate to the same URL with a changed scheme. - */ - protocol: string; - /** - * Returns the Location object's URL's query (includes leading "?" if - * non-empty). Can be set, to navigate to the same URL with a changed query - * (ignores leading "?"). - */ - search: string; - /** - * Navigates to the given URL. - */ - assign(url: string): void; - /** - * Reloads the current page. - */ - reload(): void; - /** @deprecated */ - reload(forcedReload: boolean): void; - /** - * Removes the current page from the session history and navigates to the - * given URL. - */ - replace(url: string): void; -} diff --git a/js/dom_util.ts b/js/dom_util.ts deleted file mode 100644 index 725a35aaf..000000000 --- a/js/dom_util.ts +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -// Utility functions for DOM nodes -import * as domTypes from "./dom_types.ts"; - -export function isNode(nodeImpl: domTypes.EventTarget | null): boolean { - return Boolean(nodeImpl && "nodeType" in nodeImpl); -} - -export function isShadowRoot(nodeImpl: domTypes.EventTarget | null): boolean { - return Boolean( - nodeImpl && - nodeImpl[domTypes.eventTargetNodeType] === - domTypes.NodeType.DOCUMENT_FRAGMENT_NODE && - nodeImpl[domTypes.eventTargetHost] != null - ); -} - -export function isSlotable(nodeImpl: domTypes.EventTarget | null): boolean { - return Boolean( - nodeImpl && - (nodeImpl[domTypes.eventTargetNodeType] === - domTypes.NodeType.ELEMENT_NODE || - nodeImpl[domTypes.eventTargetNodeType] === domTypes.NodeType.TEXT_NODE) - ); -} - -// https://dom.spec.whatwg.org/#node-trees -// const domSymbolTree = Symbol("DOM Symbol Tree"); - -// https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor -export function isShadowInclusiveAncestor( - ancestor: domTypes.EventTarget | null, - node: domTypes.EventTarget | null -): boolean { - while (isNode(node)) { - if (node === ancestor) { - return true; - } - - if (isShadowRoot(node)) { - node = node && node[domTypes.eventTargetHost]; - } else { - node = null; // domSymbolTree.parent(node); - } - } - - return false; -} - -export function getRoot( - node: domTypes.EventTarget | null -): domTypes.EventTarget | null { - const root = node; - - // for (const ancestor of domSymbolTree.ancestorsIterator(node)) { - // root = ancestor; - // } - - return root; -} - -// https://dom.spec.whatwg.org/#retarget -export function retarget( - a: domTypes.EventTarget | null, - b: domTypes.EventTarget -): domTypes.EventTarget | null { - while (true) { - if (!isNode(a)) { - return a; - } - - const aRoot = getRoot(a); - - if (aRoot) { - if ( - !isShadowRoot(aRoot) || - (isNode(b) && isShadowInclusiveAncestor(aRoot, b)) - ) { - return a; - } - - a = aRoot[domTypes.eventTargetHost]; - } - } -} diff --git a/js/error_stack.ts b/js/error_stack.ts deleted file mode 100644 index 98b0b02d4..000000000 --- a/js/error_stack.ts +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -// Some of the code here is adapted directly from V8 and licensed under a BSD -// style license available here: https://github.com/v8/v8/blob/24886f2d1c565287d33d71e4109a53bf0b54b75c/LICENSE.v8 -import * as dispatch from "./dispatch.ts"; -import { sendSync } from "./dispatch_json.ts"; -import { assert } from "./util.ts"; - -export interface Location { - /** The full url for the module, e.g. `file://some/file.ts` or - * `https://some/file.ts`. */ - filename: string; - - /** The line number in the file. It is assumed to be 1-indexed. */ - line: number; - - /** The column number in the file. It is assumed to be 1-indexed. */ - column: number; -} - -/** Given a current location in a module, lookup the source location and - * return it. - * - * When Deno transpiles code, it keep source maps of the transpiled code. This - * function can be used to lookup the original location. This is automatically - * done when accessing the `.stack` of an error, or when an uncaught error is - * logged. This function can be used to perform the lookup for creating better - * error handling. - * - * **Note:** `line` and `column` are 1 indexed, which matches display - * expectations, but is not typical of most index numbers in Deno. - * - * An example: - * - * const orig = Deno.applySourceMap({ - * location: "file://my/module.ts", - * line: 5, - * column: 15 - * }); - * console.log(`${orig.filename}:${orig.line}:${orig.column}`); - * - */ -export function applySourceMap(location: Location): Location { - const { filename, line, column } = location; - // On this side, line/column are 1 based, but in the source maps, they are - // 0 based, so we have to convert back and forth - const res = sendSync(dispatch.OP_APPLY_SOURCE_MAP, { - filename, - line: line - 1, - column: column - 1 - }); - return { - filename: res.filename, - line: res.line + 1, - column: res.column + 1 - }; -} - -/** Mutate the call site so that it returns the location, instead of its - * original location. - */ -function patchCallSite(callSite: CallSite, location: Location): CallSite { - return { - getThis(): unknown { - return callSite.getThis(); - }, - getTypeName(): string { - return callSite.getTypeName(); - }, - getFunction(): Function { - return callSite.getFunction(); - }, - getFunctionName(): string { - return callSite.getFunctionName(); - }, - getMethodName(): string { - return callSite.getMethodName(); - }, - getFileName(): string { - return location.filename; - }, - getLineNumber(): number { - return location.line; - }, - getColumnNumber(): number { - return location.column; - }, - getEvalOrigin(): string | null { - return callSite.getEvalOrigin(); - }, - isToplevel(): boolean { - return callSite.isToplevel(); - }, - isEval(): boolean { - return callSite.isEval(); - }, - isNative(): boolean { - return callSite.isNative(); - }, - isConstructor(): boolean { - return callSite.isConstructor(); - }, - isAsync(): boolean { - return callSite.isAsync(); - }, - isPromiseAll(): boolean { - return callSite.isPromiseAll(); - }, - getPromiseIndex(): number | null { - return callSite.getPromiseIndex(); - } - }; -} - -/** Return a string representations of a CallSite's method call name - * - * This is adapted directly from V8. - */ -function getMethodCall(callSite: CallSite): string { - let result = ""; - - const typeName = callSite.getTypeName(); - const methodName = callSite.getMethodName(); - const functionName = callSite.getFunctionName(); - - if (functionName) { - if (typeName) { - const startsWithTypeName = functionName.startsWith(typeName); - if (!startsWithTypeName) { - result += `${typeName}.`; - } - } - result += functionName; - - if (methodName) { - if (!functionName.endsWith(methodName)) { - result += ` [as ${methodName}]`; - } - } - } else { - if (typeName) { - result += `${typeName}.`; - } - if (methodName) { - result += methodName; - } else { - result += "<anonymous>"; - } - } - - return result; -} - -/** Return a string representations of a CallSite's file location - * - * This is adapted directly from V8. - */ -function getFileLocation(callSite: CallSite): string { - if (callSite.isNative()) { - return "native"; - } - - let result = ""; - - const fileName = callSite.getFileName(); - if (!fileName && callSite.isEval()) { - const evalOrigin = callSite.getEvalOrigin(); - assert(evalOrigin != null); - result += `${evalOrigin}, `; - } - - if (fileName) { - result += fileName; - } else { - result += "<anonymous>"; - } - - const lineNumber = callSite.getLineNumber(); - if (lineNumber != null) { - result += `:${lineNumber}`; - - const columnNumber = callSite.getColumnNumber(); - if (columnNumber != null) { - result += `:${columnNumber}`; - } - } - - return result; -} - -/** Convert a CallSite to a string. - * - * This is adapted directly from V8. - */ -function callSiteToString(callSite: CallSite): string { - let result = ""; - const functionName = callSite.getFunctionName(); - - const isTopLevel = callSite.isToplevel(); - const isAsync = callSite.isAsync(); - const isPromiseAll = callSite.isPromiseAll(); - const isConstructor = callSite.isConstructor(); - const isMethodCall = !(isTopLevel || isConstructor); - - if (isAsync) { - result += "async "; - } - if (isPromiseAll) { - result += `Promise.all (index ${callSite.getPromiseIndex})`; - return result; - } - if (isMethodCall) { - result += getMethodCall(callSite); - } else if (isConstructor) { - result += "new "; - if (functionName) { - result += functionName; - } else { - result += "<anonymous>"; - } - } else if (functionName) { - result += functionName; - } else { - result += getFileLocation(callSite); - return result; - } - - result += ` (${getFileLocation(callSite)})`; - return result; -} - -/** A replacement for the default stack trace preparer which will op into Rust - * to apply source maps to individual sites - */ -function prepareStackTrace( - error: Error, - structuredStackTrace: CallSite[] -): string { - return ( - `${error.name}: ${error.message}\n` + - structuredStackTrace - .map( - (callSite): CallSite => { - const filename = callSite.getFileName(); - const line = callSite.getLineNumber(); - const column = callSite.getColumnNumber(); - if (filename && line != null && column != null) { - return patchCallSite( - callSite, - applySourceMap({ - filename, - line, - column - }) - ); - } - return callSite; - } - ) - .map((callSite): string => ` at ${callSiteToString(callSite)}`) - .join("\n") - ); -} - -/** Sets the `prepareStackTrace` method on the Error constructor which will - * op into Rust to remap source code for caught errors where the `.stack` is - * being accessed. - * - * See: https://v8.dev/docs/stack-trace-api - */ -// @internal -export function setPrepareStackTrace(ErrorConstructor: typeof Error): void { - ErrorConstructor.prepareStackTrace = prepareStackTrace; -} diff --git a/js/error_stack_test.ts b/js/error_stack_test.ts deleted file mode 100644 index 4c7edb2fd..000000000 --- a/js/error_stack_test.ts +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, assert } from "./test_util.ts"; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const { setPrepareStackTrace } = Deno as any; - -interface CallSite { - getThis(): unknown; - getTypeName(): string; - getFunction(): Function; - getFunctionName(): string; - getMethodName(): string; - getFileName(): string; - getLineNumber(): number | null; - getColumnNumber(): number | null; - getEvalOrigin(): string | null; - isToplevel(): boolean; - isEval(): boolean; - isNative(): boolean; - isConstructor(): boolean; - isAsync(): boolean; - isPromiseAll(): boolean; - getPromiseIndex(): number | null; -} - -function getMockCallSite( - filename: string, - line: number | null, - column: number | null -): CallSite { - return { - getThis(): unknown { - return undefined; - }, - getTypeName(): string { - return ""; - }, - getFunction(): Function { - return (): void => {}; - }, - getFunctionName(): string { - return ""; - }, - getMethodName(): string { - return ""; - }, - getFileName(): string { - return filename; - }, - getLineNumber(): number | null { - return line; - }, - getColumnNumber(): number | null { - return column; - }, - getEvalOrigin(): null { - return null; - }, - isToplevel(): false { - return false; - }, - isEval(): false { - return false; - }, - isNative(): false { - return false; - }, - isConstructor(): false { - return false; - }, - isAsync(): false { - return false; - }, - isPromiseAll(): false { - return false; - }, - getPromiseIndex(): null { - return null; - } - }; -} - -test(function prepareStackTrace(): void { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const MockError = {} as any; - setPrepareStackTrace(MockError); - assert(typeof MockError.prepareStackTrace === "function"); - const prepareStackTrace: ( - error: Error, - structuredStackTrace: CallSite[] - ) => string = MockError.prepareStackTrace; - const result = prepareStackTrace(new Error("foo"), [ - getMockCallSite("CLI_SNAPSHOT.js", 23, 0) - ]); - assert(result.startsWith("Error: foo\n")); - assert(result.includes(".ts:"), "should remap to something in 'js/'"); -}); - -test(function applySourceMap(): void { - const result = Deno.applySourceMap({ - filename: "CLI_SNAPSHOT.js", - line: 23, - column: 0 - }); - assert(result.filename.endsWith(".ts")); - assert(result.line != null); - assert(result.column != null); -}); diff --git a/js/errors.ts b/js/errors.ts deleted file mode 100644 index 02ddfa2f2..000000000 --- a/js/errors.ts +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. - -/** A Deno specific error. The `kind` property is set to a specific error code - * which can be used to in application logic. - * - * try { - * somethingThatMightThrow(); - * } catch (e) { - * if ( - * e instanceof Deno.DenoError && - * e.kind === Deno.ErrorKind.Overflow - * ) { - * console.error("Overflow error!"); - * } - * } - * - */ -export class DenoError<T extends ErrorKind> extends Error { - constructor(readonly kind: T, msg: string) { - super(msg); - this.name = ErrorKind[kind]; - } -} - -// Warning! The values in this enum are duplicated in cli/msg.rs -// Update carefully! -export enum ErrorKind { - NoError = 0, - NotFound = 1, - PermissionDenied = 2, - ConnectionRefused = 3, - ConnectionReset = 4, - ConnectionAborted = 5, - NotConnected = 6, - AddrInUse = 7, - AddrNotAvailable = 8, - BrokenPipe = 9, - AlreadyExists = 10, - WouldBlock = 11, - InvalidInput = 12, - InvalidData = 13, - TimedOut = 14, - Interrupted = 15, - WriteZero = 16, - Other = 17, - UnexpectedEof = 18, - BadResource = 19, - CommandFailed = 20, - EmptyHost = 21, - IdnaError = 22, - InvalidPort = 23, - InvalidIpv4Address = 24, - InvalidIpv6Address = 25, - InvalidDomainCharacter = 26, - RelativeUrlWithoutBase = 27, - RelativeUrlWithCannotBeABaseBase = 28, - SetHostOnCannotBeABaseUrl = 29, - Overflow = 30, - HttpUser = 31, - HttpClosed = 32, - HttpCanceled = 33, - HttpParse = 34, - HttpOther = 35, - TooLarge = 36, - InvalidUri = 37, - InvalidSeekMode = 38, - OpNotAvailable = 39, - WorkerInitFailed = 40, - UnixError = 41, - NoAsyncSupport = 42, - NoSyncSupport = 43, - ImportMapError = 44, - InvalidPath = 45, - ImportPrefixMissing = 46, - UnsupportedFetchScheme = 47, - TooManyRedirects = 48, - Diagnostic = 49, - JSError = 50 -} diff --git a/js/event.ts b/js/event.ts deleted file mode 100644 index 3efc1c517..000000000 --- a/js/event.ts +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as domTypes from "./dom_types.ts"; -import { getPrivateValue, requiredArguments } from "./util.ts"; - -// WeakMaps are recommended for private attributes (see MDN link below) -// https://developer.mozilla.org/en-US/docs/Archive/Add-ons/Add-on_SDK/Guides/Contributor_s_Guide/Private_Properties#Using_WeakMaps -export const eventAttributes = new WeakMap(); - -function isTrusted(this: Event): boolean { - return getPrivateValue(this, eventAttributes, "isTrusted"); -} - -export class Event implements domTypes.Event { - // The default value is `false`. - // Use `defineProperty` to define on each instance, NOT on the prototype. - isTrusted!: boolean; - // Each event has the following associated flags - private _canceledFlag = false; - private _dispatchedFlag = false; - private _initializedFlag = false; - private _inPassiveListenerFlag = false; - private _stopImmediatePropagationFlag = false; - private _stopPropagationFlag = false; - - // Property for objects on which listeners will be invoked - private _path: domTypes.EventPath[] = []; - - constructor(type: string, eventInitDict: domTypes.EventInit = {}) { - requiredArguments("Event", arguments.length, 1); - type = String(type); - this._initializedFlag = true; - eventAttributes.set(this, { - type, - bubbles: eventInitDict.bubbles || false, - cancelable: eventInitDict.cancelable || false, - composed: eventInitDict.composed || false, - currentTarget: null, - eventPhase: domTypes.EventPhase.NONE, - isTrusted: false, - relatedTarget: null, - target: null, - timeStamp: Date.now() - }); - Reflect.defineProperty(this, "isTrusted", { - enumerable: true, - get: isTrusted - }); - } - - get bubbles(): boolean { - return getPrivateValue(this, eventAttributes, "bubbles"); - } - - get cancelBubble(): boolean { - return this._stopPropagationFlag; - } - - set cancelBubble(value: boolean) { - this._stopPropagationFlag = value; - } - - get cancelBubbleImmediately(): boolean { - return this._stopImmediatePropagationFlag; - } - - set cancelBubbleImmediately(value: boolean) { - this._stopImmediatePropagationFlag = value; - } - - get cancelable(): boolean { - return getPrivateValue(this, eventAttributes, "cancelable"); - } - - get composed(): boolean { - return getPrivateValue(this, eventAttributes, "composed"); - } - - get currentTarget(): domTypes.EventTarget { - return getPrivateValue(this, eventAttributes, "currentTarget"); - } - - set currentTarget(value: domTypes.EventTarget) { - eventAttributes.set(this, { - type: this.type, - bubbles: this.bubbles, - cancelable: this.cancelable, - composed: this.composed, - currentTarget: value, - eventPhase: this.eventPhase, - isTrusted: this.isTrusted, - relatedTarget: this.relatedTarget, - target: this.target, - timeStamp: this.timeStamp - }); - } - - get defaultPrevented(): boolean { - return this._canceledFlag; - } - - get dispatched(): boolean { - return this._dispatchedFlag; - } - - set dispatched(value: boolean) { - this._dispatchedFlag = value; - } - - get eventPhase(): number { - return getPrivateValue(this, eventAttributes, "eventPhase"); - } - - set eventPhase(value: number) { - eventAttributes.set(this, { - type: this.type, - bubbles: this.bubbles, - cancelable: this.cancelable, - composed: this.composed, - currentTarget: this.currentTarget, - eventPhase: value, - isTrusted: this.isTrusted, - relatedTarget: this.relatedTarget, - target: this.target, - timeStamp: this.timeStamp - }); - } - - get initialized(): boolean { - return this._initializedFlag; - } - - set inPassiveListener(value: boolean) { - this._inPassiveListenerFlag = value; - } - - get path(): domTypes.EventPath[] { - return this._path; - } - - set path(value: domTypes.EventPath[]) { - this._path = value; - } - - get relatedTarget(): domTypes.EventTarget { - return getPrivateValue(this, eventAttributes, "relatedTarget"); - } - - set relatedTarget(value: domTypes.EventTarget) { - eventAttributes.set(this, { - type: this.type, - bubbles: this.bubbles, - cancelable: this.cancelable, - composed: this.composed, - currentTarget: this.currentTarget, - eventPhase: this.eventPhase, - isTrusted: this.isTrusted, - relatedTarget: value, - target: this.target, - timeStamp: this.timeStamp - }); - } - - get target(): domTypes.EventTarget { - return getPrivateValue(this, eventAttributes, "target"); - } - - set target(value: domTypes.EventTarget) { - eventAttributes.set(this, { - type: this.type, - bubbles: this.bubbles, - cancelable: this.cancelable, - composed: this.composed, - currentTarget: this.currentTarget, - eventPhase: this.eventPhase, - isTrusted: this.isTrusted, - relatedTarget: this.relatedTarget, - target: value, - timeStamp: this.timeStamp - }); - } - - get timeStamp(): Date { - return getPrivateValue(this, eventAttributes, "timeStamp"); - } - - get type(): string { - return getPrivateValue(this, eventAttributes, "type"); - } - - /** Returns the event’s path (objects on which listeners will be - * invoked). This does not include nodes in shadow trees if the - * shadow root was created with its ShadowRoot.mode closed. - * - * event.composedPath(); - */ - composedPath(): domTypes.EventPath[] { - if (this._path.length === 0) { - return []; - } - - const composedPath: domTypes.EventPath[] = [ - { - item: this.currentTarget, - itemInShadowTree: false, - relatedTarget: null, - rootOfClosedTree: false, - slotInClosedTree: false, - target: null, - touchTargetList: [] - } - ]; - - let currentTargetIndex = 0; - let currentTargetHiddenSubtreeLevel = 0; - - for (let index = this._path.length - 1; index >= 0; index--) { - const { item, rootOfClosedTree, slotInClosedTree } = this._path[index]; - - if (rootOfClosedTree) { - currentTargetHiddenSubtreeLevel++; - } - - if (item === this.currentTarget) { - currentTargetIndex = index; - break; - } - - if (slotInClosedTree) { - currentTargetHiddenSubtreeLevel--; - } - } - - let currentHiddenLevel = currentTargetHiddenSubtreeLevel; - let maxHiddenLevel = currentTargetHiddenSubtreeLevel; - - for (let i = currentTargetIndex - 1; i >= 0; i--) { - const { item, rootOfClosedTree, slotInClosedTree } = this._path[i]; - - if (rootOfClosedTree) { - currentHiddenLevel++; - } - - if (currentHiddenLevel <= maxHiddenLevel) { - composedPath.unshift({ - item, - itemInShadowTree: false, - relatedTarget: null, - rootOfClosedTree: false, - slotInClosedTree: false, - target: null, - touchTargetList: [] - }); - } - - if (slotInClosedTree) { - currentHiddenLevel--; - - if (currentHiddenLevel < maxHiddenLevel) { - maxHiddenLevel = currentHiddenLevel; - } - } - } - - currentHiddenLevel = currentTargetHiddenSubtreeLevel; - maxHiddenLevel = currentTargetHiddenSubtreeLevel; - - for ( - let index = currentTargetIndex + 1; - index < this._path.length; - index++ - ) { - const { item, rootOfClosedTree, slotInClosedTree } = this._path[index]; - - if (slotInClosedTree) { - currentHiddenLevel++; - } - - if (currentHiddenLevel <= maxHiddenLevel) { - composedPath.push({ - item, - itemInShadowTree: false, - relatedTarget: null, - rootOfClosedTree: false, - slotInClosedTree: false, - target: null, - touchTargetList: [] - }); - } - - if (rootOfClosedTree) { - currentHiddenLevel--; - - if (currentHiddenLevel < maxHiddenLevel) { - maxHiddenLevel = currentHiddenLevel; - } - } - } - - return composedPath; - } - - /** Cancels the event (if it is cancelable). - * See https://dom.spec.whatwg.org/#set-the-canceled-flag - * - * event.preventDefault(); - */ - preventDefault(): void { - if (this.cancelable && !this._inPassiveListenerFlag) { - this._canceledFlag = true; - } - } - - /** Stops the propagation of events further along in the DOM. - * - * event.stopPropagation(); - */ - stopPropagation(): void { - this._stopPropagationFlag = true; - } - - /** For this particular event, no other listener will be called. - * Neither those attached on the same element, nor those attached - * on elements which will be traversed later (in capture phase, - * for instance). - * - * event.stopImmediatePropagation(); - */ - stopImmediatePropagation(): void { - this._stopPropagationFlag = true; - this._stopImmediatePropagationFlag = true; - } -} - -/** Built-in objects providing `get` methods for our - * interceptable JavaScript operations. - */ -Reflect.defineProperty(Event.prototype, "bubbles", { enumerable: true }); -Reflect.defineProperty(Event.prototype, "cancelable", { enumerable: true }); -Reflect.defineProperty(Event.prototype, "composed", { enumerable: true }); -Reflect.defineProperty(Event.prototype, "currentTarget", { enumerable: true }); -Reflect.defineProperty(Event.prototype, "defaultPrevented", { - enumerable: true -}); -Reflect.defineProperty(Event.prototype, "dispatched", { enumerable: true }); -Reflect.defineProperty(Event.prototype, "eventPhase", { enumerable: true }); -Reflect.defineProperty(Event.prototype, "target", { enumerable: true }); -Reflect.defineProperty(Event.prototype, "timeStamp", { enumerable: true }); -Reflect.defineProperty(Event.prototype, "type", { enumerable: true }); diff --git a/js/event_target.ts b/js/event_target.ts deleted file mode 100644 index 08c39544c..000000000 --- a/js/event_target.ts +++ /dev/null @@ -1,503 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as domTypes from "./dom_types.ts"; -import { DenoError, ErrorKind } from "./errors.ts"; -import { hasOwnProperty, requiredArguments } from "./util.ts"; -import { - getRoot, - isNode, - isShadowRoot, - isShadowInclusiveAncestor, - isSlotable, - retarget -} from "./dom_util.ts"; -import { window } from "./window.ts"; - -// https://dom.spec.whatwg.org/#get-the-parent -// Note: Nodes, shadow roots, and documents override this algorithm so we set it to null. -function getEventTargetParent( - _eventTarget: domTypes.EventTarget, - _event: domTypes.Event -): null { - return null; -} - -export const eventTargetAssignedSlot: unique symbol = Symbol(); -export const eventTargetHasActivationBehavior: unique symbol = Symbol(); - -export class EventTarget implements domTypes.EventTarget { - public [domTypes.eventTargetHost]: domTypes.EventTarget | null = null; - public [domTypes.eventTargetListeners]: { - [type in string]: domTypes.EventListener[] - } = {}; - public [domTypes.eventTargetMode] = ""; - public [domTypes.eventTargetNodeType]: domTypes.NodeType = - domTypes.NodeType.DOCUMENT_FRAGMENT_NODE; - private [eventTargetAssignedSlot] = false; - private [eventTargetHasActivationBehavior] = false; - - public addEventListener( - type: string, - callback: (event: domTypes.Event) => void | null, - options?: domTypes.AddEventListenerOptions | boolean - ): void { - const this_ = this || window; - - requiredArguments("EventTarget.addEventListener", arguments.length, 2); - const normalizedOptions: domTypes.AddEventListenerOptions = eventTargetHelpers.normalizeAddEventHandlerOptions( - options - ); - - if (callback === null) { - return; - } - - const listeners = this_[domTypes.eventTargetListeners]; - - if (!hasOwnProperty(listeners, type)) { - listeners[type] = []; - } - - for (let i = 0; i < listeners[type].length; ++i) { - const listener = listeners[type][i]; - if ( - ((typeof listener.options === "boolean" && - listener.options === normalizedOptions.capture) || - (typeof listener.options === "object" && - listener.options.capture === normalizedOptions.capture)) && - listener.callback === callback - ) { - return; - } - } - - // eslint-disable-next-line @typescript-eslint/no-this-alias - const eventTarget = this; - listeners[type].push({ - callback, - options: normalizedOptions, - handleEvent(event: domTypes.Event): void { - this.callback.call(eventTarget, event); - } - } as domTypes.EventListener); - } - - public removeEventListener( - type: string, - callback: (event: domTypes.Event) => void | null, - options?: domTypes.EventListenerOptions | boolean - ): void { - const this_ = this || window; - - requiredArguments("EventTarget.removeEventListener", arguments.length, 2); - const listeners = this_[domTypes.eventTargetListeners]; - if (hasOwnProperty(listeners, type) && callback !== null) { - listeners[type] = listeners[type].filter( - (listener): boolean => listener.callback !== callback - ); - } - - const normalizedOptions: domTypes.EventListenerOptions = eventTargetHelpers.normalizeEventHandlerOptions( - options - ); - - if (callback === null) { - // Optimization, not in the spec. - return; - } - - if (!listeners[type]) { - return; - } - - for (let i = 0; i < listeners[type].length; ++i) { - const listener = listeners[type][i]; - - if ( - ((typeof listener.options === "boolean" && - listener.options === normalizedOptions.capture) || - (typeof listener.options === "object" && - listener.options.capture === normalizedOptions.capture)) && - listener.callback === callback - ) { - listeners[type].splice(i, 1); - break; - } - } - } - - public dispatchEvent(event: domTypes.Event): boolean { - const this_ = this || window; - - requiredArguments("EventTarget.dispatchEvent", arguments.length, 1); - const listeners = this_[domTypes.eventTargetListeners]; - if (!hasOwnProperty(listeners, event.type)) { - return true; - } - - if (event.dispatched || !event.initialized) { - throw new DenoError( - ErrorKind.InvalidData, - "Tried to dispatch an uninitialized event" - ); - } - - if (event.eventPhase !== domTypes.EventPhase.NONE) { - throw new DenoError( - ErrorKind.InvalidData, - "Tried to dispatch a dispatching event" - ); - } - - return eventTargetHelpers.dispatch(this_, event); - } - - get [Symbol.toStringTag](): string { - return "EventTarget"; - } -} - -const eventTargetHelpers = { - // https://dom.spec.whatwg.org/#concept-event-dispatch - dispatch( - targetImpl: EventTarget, - eventImpl: domTypes.Event, - targetOverride?: domTypes.EventTarget - ): boolean { - let clearTargets = false; - let activationTarget = null; - - eventImpl.dispatched = true; - - targetOverride = targetOverride || targetImpl; - let relatedTarget = retarget(eventImpl.relatedTarget, targetImpl); - - if ( - targetImpl !== relatedTarget || - targetImpl === eventImpl.relatedTarget - ) { - const touchTargets: domTypes.EventTarget[] = []; - - eventTargetHelpers.appendToEventPath( - eventImpl, - targetImpl, - targetOverride, - relatedTarget, - touchTargets, - false - ); - - const isActivationEvent = eventImpl.type === "click"; - - if (isActivationEvent && targetImpl[eventTargetHasActivationBehavior]) { - activationTarget = targetImpl; - } - - let slotInClosedTree = false; - let slotable = - isSlotable(targetImpl) && targetImpl[eventTargetAssignedSlot] - ? targetImpl - : null; - let parent = getEventTargetParent(targetImpl, eventImpl); - - // Populate event path - // https://dom.spec.whatwg.org/#event-path - while (parent !== null) { - if (slotable !== null) { - slotable = null; - - const parentRoot = getRoot(parent); - if ( - isShadowRoot(parentRoot) && - parentRoot && - parentRoot[domTypes.eventTargetMode] === "closed" - ) { - slotInClosedTree = true; - } - } - - relatedTarget = retarget(eventImpl.relatedTarget, parent); - - if ( - isNode(parent) && - isShadowInclusiveAncestor(getRoot(targetImpl), parent) - ) { - eventTargetHelpers.appendToEventPath( - eventImpl, - parent, - null, - relatedTarget, - touchTargets, - slotInClosedTree - ); - } else if (parent === relatedTarget) { - parent = null; - } else { - targetImpl = parent; - - if ( - isActivationEvent && - activationTarget === null && - targetImpl[eventTargetHasActivationBehavior] - ) { - activationTarget = targetImpl; - } - - eventTargetHelpers.appendToEventPath( - eventImpl, - parent, - targetImpl, - relatedTarget, - touchTargets, - slotInClosedTree - ); - } - - if (parent !== null) { - parent = getEventTargetParent(parent, eventImpl); - } - - slotInClosedTree = false; - } - - let clearTargetsTupleIndex = -1; - for ( - let i = eventImpl.path.length - 1; - i >= 0 && clearTargetsTupleIndex === -1; - i-- - ) { - if (eventImpl.path[i].target !== null) { - clearTargetsTupleIndex = i; - } - } - const clearTargetsTuple = eventImpl.path[clearTargetsTupleIndex]; - - clearTargets = - (isNode(clearTargetsTuple.target) && - isShadowRoot(getRoot(clearTargetsTuple.target))) || - (isNode(clearTargetsTuple.relatedTarget) && - isShadowRoot(getRoot(clearTargetsTuple.relatedTarget))); - - eventImpl.eventPhase = domTypes.EventPhase.CAPTURING_PHASE; - - for (let i = eventImpl.path.length - 1; i >= 0; --i) { - const tuple = eventImpl.path[i]; - - if (tuple.target === null) { - eventTargetHelpers.invokeEventListeners(targetImpl, tuple, eventImpl); - } - } - - for (let i = 0; i < eventImpl.path.length; i++) { - const tuple = eventImpl.path[i]; - - if (tuple.target !== null) { - eventImpl.eventPhase = domTypes.EventPhase.AT_TARGET; - } else { - eventImpl.eventPhase = domTypes.EventPhase.BUBBLING_PHASE; - } - - if ( - (eventImpl.eventPhase === domTypes.EventPhase.BUBBLING_PHASE && - eventImpl.bubbles) || - eventImpl.eventPhase === domTypes.EventPhase.AT_TARGET - ) { - eventTargetHelpers.invokeEventListeners(targetImpl, tuple, eventImpl); - } - } - } - - eventImpl.eventPhase = domTypes.EventPhase.NONE; - - eventImpl.currentTarget = null; - eventImpl.path = []; - eventImpl.dispatched = false; - eventImpl.cancelBubble = false; - eventImpl.cancelBubbleImmediately = false; - - if (clearTargets) { - eventImpl.target = null; - eventImpl.relatedTarget = null; - } - - // TODO: invoke activation targets if HTML nodes will be implemented - // if (activationTarget !== null) { - // if (!eventImpl.defaultPrevented) { - // activationTarget._activationBehavior(); - // } - // } - - return !eventImpl.defaultPrevented; - }, - - // https://dom.spec.whatwg.org/#concept-event-listener-invoke - invokeEventListeners( - targetImpl: EventTarget, - tuple: domTypes.EventPath, - eventImpl: domTypes.Event - ): void { - const tupleIndex = eventImpl.path.indexOf(tuple); - for (let i = tupleIndex; i >= 0; i--) { - const t = eventImpl.path[i]; - if (t.target) { - eventImpl.target = t.target; - break; - } - } - - eventImpl.relatedTarget = tuple.relatedTarget; - - if (eventImpl.cancelBubble) { - return; - } - - eventImpl.currentTarget = tuple.item; - - eventTargetHelpers.innerInvokeEventListeners( - targetImpl, - eventImpl, - tuple.item[domTypes.eventTargetListeners] - ); - }, - - // https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke - innerInvokeEventListeners( - targetImpl: EventTarget, - eventImpl: domTypes.Event, - targetListeners: { [type in string]: domTypes.EventListener[] } - ): boolean { - let found = false; - - const { type } = eventImpl; - - if (!targetListeners || !targetListeners[type]) { - return found; - } - - // Copy event listeners before iterating since the list can be modified during the iteration. - const handlers = targetListeners[type].slice(); - - for (let i = 0; i < handlers.length; i++) { - const listener = handlers[i]; - - let capture, once, passive; - if (typeof listener.options === "boolean") { - capture = listener.options; - once = false; - passive = false; - } else { - capture = listener.options.capture; - once = listener.options.once; - passive = listener.options.passive; - } - - // Check if the event listener has been removed since the listeners has been cloned. - if (!targetListeners[type].includes(listener)) { - continue; - } - - found = true; - - if ( - (eventImpl.eventPhase === domTypes.EventPhase.CAPTURING_PHASE && - !capture) || - (eventImpl.eventPhase === domTypes.EventPhase.BUBBLING_PHASE && capture) - ) { - continue; - } - - if (once) { - targetListeners[type].splice( - targetListeners[type].indexOf(listener), - 1 - ); - } - - if (passive) { - eventImpl.inPassiveListener = true; - } - - try { - if (listener.callback) { - listener.handleEvent(eventImpl); - } - } catch (error) { - throw new DenoError(ErrorKind.Interrupted, error.message); - } - - eventImpl.inPassiveListener = false; - - if (eventImpl.cancelBubbleImmediately) { - return found; - } - } - - return found; - }, - - normalizeAddEventHandlerOptions( - options: boolean | domTypes.AddEventListenerOptions | undefined - ): domTypes.AddEventListenerOptions { - if (typeof options === "boolean" || typeof options === "undefined") { - const returnValue: domTypes.AddEventListenerOptions = { - capture: Boolean(options), - once: false, - passive: false - }; - - return returnValue; - } else { - return options; - } - }, - - normalizeEventHandlerOptions( - options: boolean | domTypes.EventListenerOptions | undefined - ): domTypes.EventListenerOptions { - if (typeof options === "boolean" || typeof options === "undefined") { - const returnValue: domTypes.EventListenerOptions = { - capture: Boolean(options) - }; - - return returnValue; - } else { - return options; - } - }, - - // https://dom.spec.whatwg.org/#concept-event-path-append - appendToEventPath( - eventImpl: domTypes.Event, - target: domTypes.EventTarget, - targetOverride: domTypes.EventTarget | null, - relatedTarget: domTypes.EventTarget | null, - touchTargets: domTypes.EventTarget[], - slotInClosedTree: boolean - ): void { - const itemInShadowTree = isNode(target) && isShadowRoot(getRoot(target)); - const rootOfClosedTree = - isShadowRoot(target) && target[domTypes.eventTargetMode] === "closed"; - - eventImpl.path.push({ - item: target, - itemInShadowTree, - target: targetOverride, - relatedTarget, - touchTargetList: touchTargets, - rootOfClosedTree, - slotInClosedTree - }); - } -}; - -/** Built-in objects providing `get` methods for our - * interceptable JavaScript operations. - */ -Reflect.defineProperty(EventTarget.prototype, "addEventListener", { - enumerable: true -}); -Reflect.defineProperty(EventTarget.prototype, "removeEventListener", { - enumerable: true -}); -Reflect.defineProperty(EventTarget.prototype, "dispatchEvent", { - enumerable: true -}); diff --git a/js/event_target_test.ts b/js/event_target_test.ts deleted file mode 100644 index 9d7e7974c..000000000 --- a/js/event_target_test.ts +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, assertEquals } from "./test_util.ts"; - -test(function addEventListenerTest(): void { - const document = new EventTarget(); - - assertEquals(document.addEventListener("x", null, false), undefined); - assertEquals(document.addEventListener("x", null, true), undefined); - assertEquals(document.addEventListener("x", null), undefined); -}); - -test(function constructedEventTargetCanBeUsedAsExpected(): void { - const target = new EventTarget(); - const event = new Event("foo", { bubbles: true, cancelable: false }); - let callCount = 0; - - const listener = (e): void => { - assertEquals(e, event); - ++callCount; - }; - - target.addEventListener("foo", listener); - - target.dispatchEvent(event); - assertEquals(callCount, 1); - - target.dispatchEvent(event); - assertEquals(callCount, 2); - - target.removeEventListener("foo", listener); - target.dispatchEvent(event); - assertEquals(callCount, 2); -}); - -test(function anEventTargetCanBeSubclassed(): void { - class NicerEventTarget extends EventTarget { - on(type, callback?, options?): void { - this.addEventListener(type, callback, options); - } - - off(type, callback?, options?): void { - this.removeEventListener(type, callback, options); - } - } - - const target = new NicerEventTarget(); - new Event("foo", { bubbles: true, cancelable: false }); - let callCount = 0; - - const listener = (): void => { - ++callCount; - }; - - target.on("foo", listener); - assertEquals(callCount, 0); - - target.off("foo", listener); - assertEquals(callCount, 0); -}); - -test(function removingNullEventListenerShouldSucceed(): void { - const document = new EventTarget(); - assertEquals(document.removeEventListener("x", null, false), undefined); - assertEquals(document.removeEventListener("x", null, true), undefined); - assertEquals(document.removeEventListener("x", null), undefined); -}); - -test(function constructedEventTargetUseObjectPrototype(): void { - const target = new EventTarget(); - const event = new Event("toString", { bubbles: true, cancelable: false }); - let callCount = 0; - - const listener = (e): void => { - assertEquals(e, event); - ++callCount; - }; - - target.addEventListener("toString", listener); - - target.dispatchEvent(event); - assertEquals(callCount, 1); - - target.dispatchEvent(event); - assertEquals(callCount, 2); - - target.removeEventListener("toString", listener); - target.dispatchEvent(event); - assertEquals(callCount, 2); -}); - -test(function toStringShouldBeWebCompatible(): void { - const target = new EventTarget(); - assertEquals(target.toString(), "[object EventTarget]"); -}); - -test(function dispatchEventShouldNotThrowError(): void { - let hasThrown = false; - - try { - const target = new EventTarget(); - const event = new Event("hasOwnProperty", { - bubbles: true, - cancelable: false - }); - const listener = (): void => {}; - target.addEventListener("hasOwnProperty", listener); - target.dispatchEvent(event); - } catch { - hasThrown = true; - } - - assertEquals(hasThrown, false); -}); - -test(function eventTargetThisShouldDefaultToWindow(): void { - const { - addEventListener, - dispatchEvent, - removeEventListener - } = EventTarget.prototype; - let n = 1; - const event = new Event("hello"); - const listener = (): void => { - n = 2; - }; - - addEventListener("hello", listener); - window.dispatchEvent(event); - assertEquals(n, 2); - n = 1; - removeEventListener("hello", listener); - window.dispatchEvent(event); - assertEquals(n, 1); - - window.addEventListener("hello", listener); - dispatchEvent(event); - assertEquals(n, 2); - n = 1; - window.removeEventListener("hello", listener); - dispatchEvent(event); - assertEquals(n, 1); -}); diff --git a/js/event_test.ts b/js/event_test.ts deleted file mode 100644 index 72f4f5855..000000000 --- a/js/event_test.ts +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, assertEquals, assertNotEquals } from "./test_util.ts"; - -test(function eventInitializedWithType(): void { - const type = "click"; - const event = new Event(type); - - assertEquals(event.isTrusted, false); - assertEquals(event.target, null); - assertEquals(event.currentTarget, null); - assertEquals(event.type, "click"); - assertEquals(event.bubbles, false); - assertEquals(event.cancelable, false); -}); - -test(function eventInitializedWithTypeAndDict(): void { - const init = "submit"; - const eventInit = { bubbles: true, cancelable: true } as EventInit; - const event = new Event(init, eventInit); - - assertEquals(event.isTrusted, false); - assertEquals(event.target, null); - assertEquals(event.currentTarget, null); - assertEquals(event.type, "submit"); - assertEquals(event.bubbles, true); - assertEquals(event.cancelable, true); -}); - -test(function eventComposedPathSuccess(): void { - const type = "click"; - const event = new Event(type); - const composedPath = event.composedPath(); - - assertEquals(composedPath, []); -}); - -test(function eventStopPropagationSuccess(): void { - const type = "click"; - const event = new Event(type); - - assertEquals(event.cancelBubble, false); - event.stopPropagation(); - assertEquals(event.cancelBubble, true); -}); - -test(function eventStopImmediatePropagationSuccess(): void { - const type = "click"; - const event = new Event(type); - - assertEquals(event.cancelBubble, false); - assertEquals(event.cancelBubbleImmediately, false); - event.stopImmediatePropagation(); - assertEquals(event.cancelBubble, true); - assertEquals(event.cancelBubbleImmediately, true); -}); - -test(function eventPreventDefaultSuccess(): void { - const type = "click"; - const event = new Event(type); - - assertEquals(event.defaultPrevented, false); - event.preventDefault(); - assertEquals(event.defaultPrevented, false); - - const eventInit = { bubbles: true, cancelable: true } as EventInit; - const cancelableEvent = new Event(type, eventInit); - assertEquals(cancelableEvent.defaultPrevented, false); - cancelableEvent.preventDefault(); - assertEquals(cancelableEvent.defaultPrevented, true); -}); - -test(function eventInitializedWithNonStringType(): void { - const type = undefined; - const event = new Event(type); - - assertEquals(event.isTrusted, false); - assertEquals(event.target, null); - assertEquals(event.currentTarget, null); - assertEquals(event.type, "undefined"); - assertEquals(event.bubbles, false); - assertEquals(event.cancelable, false); -}); - -// ref https://github.com/web-platform-tests/wpt/blob/master/dom/events/Event-isTrusted.any.js -test(function eventIsTrusted(): void { - const desc1 = Object.getOwnPropertyDescriptor(new Event("x"), "isTrusted"); - assertNotEquals(desc1, undefined); - assertEquals(typeof desc1.get, "function"); - - const desc2 = Object.getOwnPropertyDescriptor(new Event("x"), "isTrusted"); - assertNotEquals(desc2, undefined); - assertEquals(typeof desc2.get, "function"); - - assertEquals(desc1.get, desc2.get); -}); diff --git a/js/fetch.ts b/js/fetch.ts deleted file mode 100644 index 0a5f793a8..000000000 --- a/js/fetch.ts +++ /dev/null @@ -1,478 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { - assert, - createResolvable, - notImplemented, - isTypedArray -} from "./util.ts"; -import * as domTypes from "./dom_types.ts"; -import { TextDecoder, TextEncoder } from "./text_encoding.ts"; -import { DenoBlob, bytesSymbol as blobBytesSymbol } from "./blob.ts"; -import { Headers } from "./headers.ts"; -import * as io from "./io.ts"; -import { read, close } from "./files.ts"; -import { Buffer } from "./buffer.ts"; -import { FormData } from "./form_data.ts"; -import { URLSearchParams } from "./url_search_params.ts"; -import * as dispatch from "./dispatch.ts"; -import { sendAsync } from "./dispatch_json.ts"; - -function getHeaderValueParams(value: string): Map<string, string> { - const params = new Map(); - // Forced to do so for some Map constructor param mismatch - value - .split(";") - .slice(1) - .map((s): string[] => s.trim().split("=")) - .filter((arr): boolean => arr.length > 1) - .map(([k, v]): [string, string] => [k, v.replace(/^"([^"]*)"$/, "$1")]) - .forEach(([k, v]): Map<string, string> => params.set(k, v)); - return params; -} - -function hasHeaderValueOf(s: string, value: string): boolean { - return new RegExp(`^${value}[\t\s]*;?`).test(s); -} - -class Body implements domTypes.Body, domTypes.ReadableStream, io.ReadCloser { - private _bodyUsed = false; - private _bodyPromise: null | Promise<ArrayBuffer> = null; - private _data: ArrayBuffer | null = null; - readonly locked: boolean = false; // TODO - readonly body: null | Body = this; - - constructor(private rid: number, readonly contentType: string) {} - - private async _bodyBuffer(): Promise<ArrayBuffer> { - assert(this._bodyPromise == null); - const buf = new Buffer(); - try { - const nread = await buf.readFrom(this); - const ui8 = buf.bytes(); - assert(ui8.byteLength === nread); - this._data = ui8.buffer.slice( - ui8.byteOffset, - ui8.byteOffset + nread - ) as ArrayBuffer; - assert(this._data.byteLength === nread); - } finally { - this.close(); - } - - return this._data; - } - - async arrayBuffer(): Promise<ArrayBuffer> { - // If we've already bufferred the response, just return it. - if (this._data != null) { - return this._data; - } - - // If there is no _bodyPromise yet, start it. - if (this._bodyPromise == null) { - this._bodyPromise = this._bodyBuffer(); - } - - return this._bodyPromise; - } - - async blob(): Promise<domTypes.Blob> { - const arrayBuffer = await this.arrayBuffer(); - return new DenoBlob([arrayBuffer], { - type: this.contentType - }); - } - - // ref: https://fetch.spec.whatwg.org/#body-mixin - async formData(): Promise<domTypes.FormData> { - const formData = new FormData(); - const enc = new TextEncoder(); - if (hasHeaderValueOf(this.contentType, "multipart/form-data")) { - const params = getHeaderValueParams(this.contentType); - if (!params.has("boundary")) { - // TypeError is required by spec - throw new TypeError("multipart/form-data must provide a boundary"); - } - // ref: https://tools.ietf.org/html/rfc2046#section-5.1 - const boundary = params.get("boundary")!; - const dashBoundary = `--${boundary}`; - const delimiter = `\r\n${dashBoundary}`; - const closeDelimiter = `${delimiter}--`; - - const body = await this.text(); - let bodyParts: string[]; - const bodyEpilogueSplit = body.split(closeDelimiter); - if (bodyEpilogueSplit.length < 2) { - bodyParts = []; - } else { - // discard epilogue - const bodyEpilogueTrimmed = bodyEpilogueSplit[0]; - // first boundary treated special due to optional prefixed \r\n - const firstBoundaryIndex = bodyEpilogueTrimmed.indexOf(dashBoundary); - if (firstBoundaryIndex < 0) { - throw new TypeError("Invalid boundary"); - } - const bodyPreambleTrimmed = bodyEpilogueTrimmed - .slice(firstBoundaryIndex + dashBoundary.length) - .replace(/^[\s\r\n\t]+/, ""); // remove transport-padding CRLF - // trimStart might not be available - // Be careful! body-part allows trailing \r\n! - // (as long as it is not part of `delimiter`) - bodyParts = bodyPreambleTrimmed - .split(delimiter) - .map((s): string => s.replace(/^[\s\r\n\t]+/, "")); - // TODO: LWSP definition is actually trickier, - // but should be fine in our case since without headers - // we should just discard the part - } - for (const bodyPart of bodyParts) { - const headers = new Headers(); - const headerOctetSeperatorIndex = bodyPart.indexOf("\r\n\r\n"); - if (headerOctetSeperatorIndex < 0) { - continue; // Skip unknown part - } - const headerText = bodyPart.slice(0, headerOctetSeperatorIndex); - const octets = bodyPart.slice(headerOctetSeperatorIndex + 4); - - // TODO: use textproto.readMIMEHeader from deno_std - const rawHeaders = headerText.split("\r\n"); - for (const rawHeader of rawHeaders) { - const sepIndex = rawHeader.indexOf(":"); - if (sepIndex < 0) { - continue; // Skip this header - } - const key = rawHeader.slice(0, sepIndex); - const value = rawHeader.slice(sepIndex + 1); - headers.set(key, value); - } - if (!headers.has("content-disposition")) { - continue; // Skip unknown part - } - // Content-Transfer-Encoding Deprecated - const contentDisposition = headers.get("content-disposition")!; - const partContentType = headers.get("content-type") || "text/plain"; - // TODO: custom charset encoding (needs TextEncoder support) - // const contentTypeCharset = - // getHeaderValueParams(partContentType).get("charset") || ""; - if (!hasHeaderValueOf(contentDisposition, "form-data")) { - continue; // Skip, might not be form-data - } - const dispositionParams = getHeaderValueParams(contentDisposition); - if (!dispositionParams.has("name")) { - continue; // Skip, unknown name - } - const dispositionName = dispositionParams.get("name")!; - if (dispositionParams.has("filename")) { - const filename = dispositionParams.get("filename")!; - const blob = new DenoBlob([enc.encode(octets)], { - type: partContentType - }); - // TODO: based on spec - // https://xhr.spec.whatwg.org/#dom-formdata-append - // https://xhr.spec.whatwg.org/#create-an-entry - // Currently it does not mention how I could pass content-type - // to the internally created file object... - formData.append(dispositionName, blob, filename); - } else { - formData.append(dispositionName, octets); - } - } - return formData; - } else if ( - hasHeaderValueOf(this.contentType, "application/x-www-form-urlencoded") - ) { - // From https://github.com/github/fetch/blob/master/fetch.js - // Copyright (c) 2014-2016 GitHub, Inc. MIT License - const body = await this.text(); - try { - body - .trim() - .split("&") - .forEach( - (bytes): void => { - if (bytes) { - const split = bytes.split("="); - const name = split.shift()!.replace(/\+/g, " "); - const value = split.join("=").replace(/\+/g, " "); - formData.append( - decodeURIComponent(name), - decodeURIComponent(value) - ); - } - } - ); - } catch (e) { - throw new TypeError("Invalid form urlencoded format"); - } - return formData; - } else { - throw new TypeError("Invalid form data"); - } - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - async json(): Promise<any> { - const text = await this.text(); - return JSON.parse(text); - } - - async text(): Promise<string> { - const ab = await this.arrayBuffer(); - const decoder = new TextDecoder("utf-8"); - return decoder.decode(ab); - } - - read(p: Uint8Array): Promise<number | io.EOF> { - this._bodyUsed = true; - return read(this.rid, p); - } - - close(): void { - close(this.rid); - } - - async cancel(): Promise<void> { - return notImplemented(); - } - - getReader(): domTypes.ReadableStreamReader { - return notImplemented(); - } - - tee(): [domTypes.ReadableStream, domTypes.ReadableStream] { - return notImplemented(); - } - - [Symbol.asyncIterator](): AsyncIterableIterator<Uint8Array> { - return io.toAsyncIterator(this); - } - - get bodyUsed(): boolean { - return this._bodyUsed; - } -} - -export class Response implements domTypes.Response { - readonly type = "basic"; // TODO - readonly redirected: boolean; - headers: domTypes.Headers; - readonly trailer: Promise<domTypes.Headers>; - readonly body: Body; - - constructor( - readonly url: string, - readonly status: number, - readonly statusText: string, - headersList: Array<[string, string]>, - rid: number, - redirected_: boolean, - body_: null | Body = null - ) { - this.trailer = createResolvable(); - this.headers = new Headers(headersList); - const contentType = this.headers.get("content-type") || ""; - - if (body_ == null) { - this.body = new Body(rid, contentType); - } else { - this.body = body_; - } - - this.redirected = redirected_; - } - - async arrayBuffer(): Promise<ArrayBuffer> { - return this.body.arrayBuffer(); - } - - async blob(): Promise<domTypes.Blob> { - return this.body.blob(); - } - - async formData(): Promise<domTypes.FormData> { - return this.body.formData(); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - async json(): Promise<any> { - return this.body.json(); - } - - async text(): Promise<string> { - return this.body.text(); - } - - get ok(): boolean { - return 200 <= this.status && this.status < 300; - } - - get bodyUsed(): boolean { - return this.body.bodyUsed; - } - - clone(): domTypes.Response { - if (this.bodyUsed) { - throw new TypeError( - "Failed to execute 'clone' on 'Response': Response body is already used" - ); - } - - const iterators = this.headers.entries(); - const headersList: Array<[string, string]> = []; - for (const header of iterators) { - headersList.push(header); - } - - return new Response( - this.url, - this.status, - this.statusText, - headersList, - -1, - this.redirected, - this.body - ); - } -} - -interface FetchResponse { - bodyRid: number; - status: number; - statusText: string; - headers: Array<[string, string]>; -} - -async function sendFetchReq( - url: string, - method: string | null, - headers: domTypes.Headers | null, - body: ArrayBufferView | undefined -): Promise<FetchResponse> { - let headerArray: Array<[string, string]> = []; - if (headers) { - headerArray = Array.from(headers.entries()); - } - - let zeroCopy = undefined; - if (body) { - zeroCopy = new Uint8Array(body.buffer, body.byteOffset, body.byteLength); - } - - const args = { - method, - url, - headers: headerArray - }; - - return (await sendAsync(dispatch.OP_FETCH, args, zeroCopy)) as FetchResponse; -} - -/** Fetch a resource from the network. */ -export async function fetch( - input: domTypes.Request | string, - init?: domTypes.RequestInit -): Promise<Response> { - let url: string; - let method: string | null = null; - let headers: domTypes.Headers | null = null; - let body: ArrayBufferView | undefined; - let redirected = false; - let remRedirectCount = 20; // TODO: use a better way to handle - - if (typeof input === "string") { - url = input; - if (init != null) { - method = init.method || null; - if (init.headers) { - headers = - init.headers instanceof Headers - ? init.headers - : new Headers(init.headers); - } else { - headers = null; - } - - // ref: https://fetch.spec.whatwg.org/#body-mixin - // Body should have been a mixin - // but we are treating it as a separate class - if (init.body) { - if (!headers) { - headers = new Headers(); - } - let contentType = ""; - if (typeof init.body === "string") { - body = new TextEncoder().encode(init.body); - contentType = "text/plain;charset=UTF-8"; - } else if (isTypedArray(init.body)) { - body = init.body; - } else if (init.body instanceof URLSearchParams) { - body = new TextEncoder().encode(init.body.toString()); - contentType = "application/x-www-form-urlencoded;charset=UTF-8"; - } else if (init.body instanceof DenoBlob) { - body = init.body[blobBytesSymbol]; - contentType = init.body.type; - } else { - // TODO: FormData, ReadableStream - notImplemented(); - } - if (contentType && !headers.has("content-type")) { - headers.set("content-type", contentType); - } - } - } - } else { - url = input.url; - method = input.method; - headers = input.headers; - - //@ts-ignore - if (input._bodySource) { - body = new DataView(await input.arrayBuffer()); - } - } - - while (remRedirectCount) { - const fetchResponse = await sendFetchReq(url, method, headers, body); - - const response = new Response( - url, - fetchResponse.status, - fetchResponse.statusText, - fetchResponse.headers, - fetchResponse.bodyRid, - redirected - ); - if ([301, 302, 303, 307, 308].includes(response.status)) { - // We're in a redirect status - switch ((init && init.redirect) || "follow") { - case "error": - throw notImplemented(); - case "manual": - throw notImplemented(); - case "follow": - default: - let redirectUrl = response.headers.get("Location"); - if (redirectUrl == null) { - return response; // Unspecified - } - if ( - !redirectUrl.startsWith("http://") && - !redirectUrl.startsWith("https://") - ) { - redirectUrl = - url.split("//")[0] + - "//" + - url.split("//")[1].split("/")[0] + - redirectUrl; // TODO: handle relative redirection more gracefully - } - url = redirectUrl; - redirected = true; - remRedirectCount--; - } - } else { - return response; - } - } - // Return a network error due to too many redirections - throw notImplemented(); -} diff --git a/js/fetch_test.ts b/js/fetch_test.ts deleted file mode 100644 index 56c693681..000000000 --- a/js/fetch_test.ts +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { - test, - testPerm, - assert, - assertEquals, - assertStrContains, - assertThrows -} from "./test_util.ts"; - -testPerm({ net: true }, async function fetchConnectionError(): Promise<void> { - let err; - try { - await fetch("http://localhost:4000"); - } catch (err_) { - err = err_; - } - assertEquals(err.kind, Deno.ErrorKind.HttpOther); - assertEquals(err.name, "HttpOther"); - assertStrContains(err.message, "error trying to connect"); -}); - -testPerm({ net: true }, async function fetchJsonSuccess(): Promise<void> { - const response = await fetch("http://localhost:4545/package.json"); - const json = await response.json(); - assertEquals(json.name, "deno"); -}); - -test(async function fetchPerm(): Promise<void> { - let err; - try { - await fetch("http://localhost:4545/package.json"); - } catch (err_) { - err = err_; - } - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -testPerm({ net: true }, async function fetchUrl(): Promise<void> { - const response = await fetch("http://localhost:4545/package.json"); - assertEquals(response.url, "http://localhost:4545/package.json"); -}); - -testPerm({ net: true }, async function fetchHeaders(): Promise<void> { - const response = await fetch("http://localhost:4545/package.json"); - const headers = response.headers; - assertEquals(headers.get("Content-Type"), "application/json"); - assert(headers.get("Server").startsWith("SimpleHTTP")); -}); - -testPerm({ net: true }, async function fetchBlob(): Promise<void> { - const response = await fetch("http://localhost:4545/package.json"); - const headers = response.headers; - const blob = await response.blob(); - assertEquals(blob.type, headers.get("Content-Type")); - assertEquals(blob.size, Number(headers.get("Content-Length"))); -}); - -testPerm({ net: true }, async function fetchBodyUsed(): Promise<void> { - const response = await fetch("http://localhost:4545/package.json"); - assertEquals(response.bodyUsed, false); - assertThrows( - (): void => { - // Assigning to read-only property throws in the strict mode. - response.bodyUsed = true; - } - ); - await response.blob(); - assertEquals(response.bodyUsed, true); -}); - -testPerm({ net: true }, async function fetchAsyncIterator(): Promise<void> { - const response = await fetch("http://localhost:4545/package.json"); - const headers = response.headers; - let total = 0; - for await (const chunk of response.body) { - total += chunk.length; - } - - assertEquals(total, Number(headers.get("Content-Length"))); -}); - -testPerm({ net: true }, async function responseClone(): Promise<void> { - const response = await fetch("http://localhost:4545/package.json"); - const response1 = response.clone(); - assert(response !== response1); - assertEquals(response.status, response1.status); - assertEquals(response.statusText, response1.statusText); - const ab = await response.arrayBuffer(); - const ab1 = await response1.arrayBuffer(); - for (let i = 0; i < ab.byteLength; i++) { - assertEquals(ab[i], ab1[i]); - } -}); - -testPerm({ net: true }, async function fetchEmptyInvalid(): Promise<void> { - let err; - try { - await fetch(""); - } catch (err_) { - err = err_; - } - assertEquals(err.kind, Deno.ErrorKind.RelativeUrlWithoutBase); - assertEquals(err.name, "RelativeUrlWithoutBase"); -}); - -testPerm({ net: true }, async function fetchMultipartFormDataSuccess(): Promise< - void -> { - const response = await fetch( - "http://localhost:4545/tests/subdir/multipart_form_data.txt" - ); - const formData = await response.formData(); - assert(formData.has("field_1")); - assertEquals(formData.get("field_1").toString(), "value_1 \r\n"); - assert(formData.has("field_2")); - /* TODO(ry) Re-enable this test once we bring back the global File type. - const file = formData.get("field_2") as File; - assertEquals(file.name, "file.js"); - */ - // Currently we cannot read from file... -}); - -testPerm( - { net: true }, - async function fetchURLEncodedFormDataSuccess(): Promise<void> { - const response = await fetch( - "http://localhost:4545/tests/subdir/form_urlencoded.txt" - ); - const formData = await response.formData(); - assert(formData.has("field_1")); - assertEquals(formData.get("field_1").toString(), "Hi"); - assert(formData.has("field_2")); - assertEquals(formData.get("field_2").toString(), "<Deno>"); - } -); - -testPerm({ net: true }, async function fetchWithRedirection(): Promise<void> { - const response = await fetch("http://localhost:4546/"); // will redirect to http://localhost:4545/ - assertEquals(response.status, 200); - assertEquals(response.statusText, "OK"); - assertEquals(response.url, "http://localhost:4545/"); - const body = await response.text(); - assert(body.includes("<title>Directory listing for /</title>")); -}); - -testPerm({ net: true }, async function fetchWithRelativeRedirection(): Promise< - void -> { - const response = await fetch("http://localhost:4545/tests"); // will redirect to /tests/ - assertEquals(response.status, 200); - assertEquals(response.statusText, "OK"); - const body = await response.text(); - assert(body.includes("<title>Directory listing for /tests/</title>")); -}); - -// The feature below is not implemented, but the test should work after implementation -/* -testPerm({ net: true }, async function fetchWithInfRedirection(): Promise< - void -> { - const response = await fetch("http://localhost:4549/tests"); // will redirect to the same place - assertEquals(response.status, 0); // network error -}); -*/ - -testPerm({ net: true }, async function fetchInitStringBody(): Promise<void> { - const data = "Hello World"; - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: data - }); - const text = await response.text(); - assertEquals(text, data); - assert(response.headers.get("content-type").startsWith("text/plain")); -}); - -testPerm({ net: true }, async function fetchRequestInitStringBody(): Promise< - void -> { - const data = "Hello World"; - const req = new Request("http://localhost:4545/echo_server", { - method: "POST", - body: data - }); - const response = await fetch(req); - const text = await response.text(); - assertEquals(text, data); -}); - -testPerm({ net: true }, async function fetchInitTypedArrayBody(): Promise< - void -> { - const data = "Hello World"; - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: new TextEncoder().encode(data) - }); - const text = await response.text(); - assertEquals(text, data); -}); - -testPerm({ net: true }, async function fetchInitURLSearchParamsBody(): Promise< - void -> { - const data = "param1=value1¶m2=value2"; - const params = new URLSearchParams(data); - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: params - }); - const text = await response.text(); - assertEquals(text, data); - assert( - response.headers - .get("content-type") - .startsWith("application/x-www-form-urlencoded") - ); -}); - -testPerm({ net: true }, async function fetchInitBlobBody(): Promise<void> { - const data = "const a = 1"; - const blob = new Blob([data], { - type: "text/javascript" - }); - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: blob - }); - const text = await response.text(); - assertEquals(text, data); - assert(response.headers.get("content-type").startsWith("text/javascript")); -}); - -testPerm({ net: true }, async function fetchUserAgent(): Promise<void> { - const data = "Hello World"; - const response = await fetch("http://localhost:4545/echo_server", { - method: "POST", - body: new TextEncoder().encode(data) - }); - assertEquals(response.headers.get("user-agent"), `Deno/${Deno.version.deno}`); - await response.text(); -}); - -// TODO(ry) The following tests work but are flaky. There's a race condition -// somewhere. Here is what one of these flaky failures looks like: -// -// test fetchPostBodyString_permW0N1E0R0 -// assertEquals failed. actual = expected = POST /blah HTTP/1.1 -// hello: World -// foo: Bar -// host: 127.0.0.1:4502 -// content-length: 11 -// hello world -// Error: actual: expected: POST /blah HTTP/1.1 -// hello: World -// foo: Bar -// host: 127.0.0.1:4502 -// content-length: 11 -// hello world -// at Object.assertEquals (file:///C:/deno/js/testing/util.ts:29:11) -// at fetchPostBodyString (file - -/* -function bufferServer(addr: string): Deno.Buffer { - const listener = Deno.listen(addr); - const buf = new Deno.Buffer(); - listener.accept().then(async conn => { - const p1 = buf.readFrom(conn); - const p2 = conn.write( - new TextEncoder().encode( - "HTTP/1.0 404 Not Found\r\nContent-Length: 2\r\n\r\nNF" - ) - ); - // Wait for both an EOF on the read side of the socket and for the write to - // complete before closing it. Due to keep-alive, the EOF won't be sent - // until the Connection close (HTTP/1.0) response, so readFrom() can't - // proceed write. Conversely, if readFrom() is async, waiting for the - // write() to complete is not a guarantee that we've read the incoming - // request. - await Promise.all([p1, p2]); - conn.close(); - listener.close(); - }); - return buf; -} - -testPerm({ net: true }, async function fetchRequest():Promise<void> { - const addr = "127.0.0.1:4501"; - const buf = bufferServer(addr); - const response = await fetch(`http://${addr}/blah`, { - method: "POST", - headers: [["Hello", "World"], ["Foo", "Bar"]] - }); - assertEquals(response.status, 404); - assertEquals(response.headers.get("Content-Length"), "2"); - - const actual = new TextDecoder().decode(buf.bytes()); - const expected = [ - "POST /blah HTTP/1.1\r\n", - "hello: World\r\n", - "foo: Bar\r\n", - `host: ${addr}\r\n\r\n` - ].join(""); - assertEquals(actual, expected); -}); - -testPerm({ net: true }, async function fetchPostBodyString():Promise<void> { - const addr = "127.0.0.1:4502"; - const buf = bufferServer(addr); - const body = "hello world"; - const response = await fetch(`http://${addr}/blah`, { - method: "POST", - headers: [["Hello", "World"], ["Foo", "Bar"]], - body - }); - assertEquals(response.status, 404); - assertEquals(response.headers.get("Content-Length"), "2"); - - const actual = new TextDecoder().decode(buf.bytes()); - const expected = [ - "POST /blah HTTP/1.1\r\n", - "hello: World\r\n", - "foo: Bar\r\n", - `host: ${addr}\r\n`, - `content-length: ${body.length}\r\n\r\n`, - body - ].join(""); - assertEquals(actual, expected); -}); - -testPerm({ net: true }, async function fetchPostBodyTypedArray():Promise<void> { - const addr = "127.0.0.1:4503"; - const buf = bufferServer(addr); - const bodyStr = "hello world"; - const body = new TextEncoder().encode(bodyStr); - const response = await fetch(`http://${addr}/blah`, { - method: "POST", - headers: [["Hello", "World"], ["Foo", "Bar"]], - body - }); - assertEquals(response.status, 404); - assertEquals(response.headers.get("Content-Length"), "2"); - - const actual = new TextDecoder().decode(buf.bytes()); - const expected = [ - "POST /blah HTTP/1.1\r\n", - "hello: World\r\n", - "foo: Bar\r\n", - `host: ${addr}\r\n`, - `content-length: ${body.byteLength}\r\n\r\n`, - bodyStr - ].join(""); - assertEquals(actual, expected); -}); -*/ diff --git a/js/file_info.ts b/js/file_info.ts deleted file mode 100644 index a98989e79..000000000 --- a/js/file_info.ts +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { StatResponse } from "./stat.ts"; - -/** A FileInfo describes a file and is returned by `stat`, `lstat`, - * `statSync`, `lstatSync`. - */ -export interface FileInfo { - /** The size of the file, in bytes. */ - len: number; - /** The last modification time of the file. This corresponds to the `mtime` - * field from `stat` on Unix and `ftLastWriteTime` on Windows. This may not - * be available on all platforms. - */ - modified: number | null; - /** The last access time of the file. This corresponds to the `atime` - * field from `stat` on Unix and `ftLastAccessTime` on Windows. This may not - * be available on all platforms. - */ - accessed: number | null; - /** The last access time of the file. This corresponds to the `birthtime` - * field from `stat` on Unix and `ftCreationTime` on Windows. This may not - * be available on all platforms. - */ - created: number | null; - /** The underlying raw st_mode bits that contain the standard Unix permissions - * for this file/directory. TODO Match behavior with Go on windows for mode. - */ - mode: number | null; - - /** The file or directory name. */ - name: string | null; - - /** Returns whether this is info for a regular file. This result is mutually - * exclusive to `FileInfo.isDirectory` and `FileInfo.isSymlink`. - */ - isFile(): boolean; - - /** Returns whether this is info for a regular directory. This result is - * mutually exclusive to `FileInfo.isFile` and `FileInfo.isSymlink`. - */ - isDirectory(): boolean; - - /** Returns whether this is info for a symlink. This result is - * mutually exclusive to `FileInfo.isFile` and `FileInfo.isDirectory`. - */ - isSymlink(): boolean; -} - -// @internal -export class FileInfoImpl implements FileInfo { - private readonly _isFile: boolean; - private readonly _isSymlink: boolean; - len: number; - modified: number | null; - accessed: number | null; - created: number | null; - mode: number | null; - name: string | null; - - /* @internal */ - constructor(private _res: StatResponse) { - const modified = this._res.modified; - const accessed = this._res.accessed; - const created = this._res.created; - const hasMode = this._res.hasMode; - const mode = this._res.mode; // negative for invalid mode (Windows) - const name = this._res.name; - - this._isFile = this._res.isFile; - this._isSymlink = this._res.isSymlink; - this.len = this._res.len; - this.modified = modified ? modified : null; - this.accessed = accessed ? accessed : null; - this.created = created ? created : null; - // null on Windows - this.mode = hasMode ? mode : null; - this.name = name ? name : null; - } - - isFile(): boolean { - return this._isFile; - } - - isDirectory(): boolean { - return !this._isFile && !this._isSymlink; - } - - isSymlink(): boolean { - return this._isSymlink; - } -} diff --git a/js/file_test.ts b/js/file_test.ts deleted file mode 100644 index 345dcd8fe..000000000 --- a/js/file_test.ts +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, assert, assertEquals } from "./test_util.ts"; - -function testFirstArgument(arg1, expectedSize): void { - const file = new File(arg1, "name"); - assert(file instanceof File); - assertEquals(file.name, "name"); - assertEquals(file.size, expectedSize); - assertEquals(file.type, ""); -} - -test(function fileEmptyFileBits(): void { - testFirstArgument([], 0); -}); - -test(function fileStringFileBits(): void { - testFirstArgument(["bits"], 4); -}); - -test(function fileUnicodeStringFileBits(): void { - testFirstArgument(["𝓽𝓮𝔁𝓽"], 16); -}); - -test(function fileStringObjectFileBits(): void { - testFirstArgument([new String("string object")], 13); -}); - -test(function fileEmptyBlobFileBits(): void { - testFirstArgument([new Blob()], 0); -}); - -test(function fileBlobFileBits(): void { - testFirstArgument([new Blob(["bits"])], 4); -}); - -test(function fileEmptyFileFileBits(): void { - testFirstArgument([new File([], "world.txt")], 0); -}); - -test(function fileFileFileBits(): void { - testFirstArgument([new File(["bits"], "world.txt")], 4); -}); - -test(function fileArrayBufferFileBits(): void { - testFirstArgument([new ArrayBuffer(8)], 8); -}); - -test(function fileTypedArrayFileBits(): void { - testFirstArgument([new Uint8Array([0x50, 0x41, 0x53, 0x53])], 4); -}); - -test(function fileVariousFileBits(): void { - testFirstArgument( - [ - "bits", - new Blob(["bits"]), - new Blob(), - new Uint8Array([0x50, 0x41]), - new Uint16Array([0x5353]), - new Uint32Array([0x53534150]) - ], - 16 - ); -}); - -test(function fileNumberInFileBits(): void { - testFirstArgument([12], 2); -}); - -test(function fileArrayInFileBits(): void { - testFirstArgument([[1, 2, 3]], 5); -}); - -test(function fileObjectInFileBits(): void { - // "[object Object]" - testFirstArgument([{}], 15); -}); - -function testSecondArgument(arg2, expectedFileName): void { - const file = new File(["bits"], arg2); - assert(file instanceof File); - assertEquals(file.name, expectedFileName); -} - -test(function fileUsingFileName(): void { - testSecondArgument("dummy", "dummy"); -}); - -test(function fileUsingSpecialCharacterInFileName(): void { - testSecondArgument("dummy/foo", "dummy:foo"); -}); - -test(function fileUsingNullFileName(): void { - testSecondArgument(null, "null"); -}); - -test(function fileUsingNumberFileName(): void { - testSecondArgument(1, "1"); -}); - -test(function fileUsingEmptyStringFileName(): void { - testSecondArgument("", ""); -}); diff --git a/js/files.ts b/js/files.ts deleted file mode 100644 index b83a147e1..000000000 --- a/js/files.ts +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { - EOF, - Reader, - Writer, - Seeker, - Closer, - SeekMode, - SyncReader, - SyncWriter, - SyncSeeker -} from "./io.ts"; -import { sendAsyncMinimal, sendSyncMinimal } from "./dispatch_minimal.ts"; -import * as dispatch from "./dispatch.ts"; -import { - sendSync as sendSyncJson, - sendAsync as sendAsyncJson -} from "./dispatch_json.ts"; - -/** Open a file and return an instance of the `File` object - * synchronously. - * - * const file = Deno.openSync("/foo/bar.txt"); - */ -export function openSync(filename: string, mode: OpenMode = "r"): File { - const rid = sendSyncJson(dispatch.OP_OPEN, { filename, mode }); - return new File(rid); -} - -/** Open a file and return an instance of the `File` object. - * - * (async () => { - * const file = await Deno.open("/foo/bar.txt"); - * })(); - */ -export async function open( - filename: string, - mode: OpenMode = "r" -): Promise<File> { - const rid = await sendAsyncJson(dispatch.OP_OPEN, { filename, mode }); - return new File(rid); -} - -/** Read synchronously from a file ID into an array buffer. - * - * Return `number | EOF` for the operation. - * - * const file = Deno.openSync("/foo/bar.txt"); - * const buf = new Uint8Array(100); - * const nread = Deno.readSync(file.rid, buf); - * const text = new TextDecoder().decode(buf); - * - */ -export function readSync(rid: number, p: Uint8Array): number | EOF { - const nread = sendSyncMinimal(dispatch.OP_READ, rid, p); - if (nread < 0) { - throw new Error("read error"); - } else if (nread == 0) { - return EOF; - } else { - return nread; - } -} - -/** Read from a file ID into an array buffer. - * - * Resolves with the `number | EOF` for the operation. - * - * (async () => { - * const file = await Deno.open("/foo/bar.txt"); - * const buf = new Uint8Array(100); - * const nread = await Deno.read(file.rid, buf); - * const text = new TextDecoder().decode(buf); - * })(); - */ -export async function read(rid: number, p: Uint8Array): Promise<number | EOF> { - const nread = await sendAsyncMinimal(dispatch.OP_READ, rid, p); - if (nread < 0) { - throw new Error("read error"); - } else if (nread == 0) { - return EOF; - } else { - return nread; - } -} - -/** Write synchronously to the file ID the contents of the array buffer. - * - * Resolves with the number of bytes written. - * - * const encoder = new TextEncoder(); - * const data = encoder.encode("Hello world\n"); - * const file = Deno.openSync("/foo/bar.txt"); - * Deno.writeSync(file.rid, data); - */ -export function writeSync(rid: number, p: Uint8Array): number { - const result = sendSyncMinimal(dispatch.OP_WRITE, rid, p); - if (result < 0) { - throw new Error("write error"); - } else { - return result; - } -} - -/** Write to the file ID the contents of the array buffer. - * - * Resolves with the number of bytes written. - * - * (async () => { - * const encoder = new TextEncoder(); - * const data = encoder.encode("Hello world\n"); - * const file = await Deno.open("/foo/bar.txt"); - * await Deno.write(file.rid, data); - * })(); - * - */ -export async function write(rid: number, p: Uint8Array): Promise<number> { - const result = await sendAsyncMinimal(dispatch.OP_WRITE, rid, p); - if (result < 0) { - throw new Error("write error"); - } else { - return result; - } -} - -/** Seek a file ID synchronously to the given offset under mode given by `whence`. - * - * const file = Deno.openSync("/foo/bar.txt"); - * Deno.seekSync(file.rid, 0, 0); - */ -export function seekSync(rid: number, offset: number, whence: SeekMode): void { - sendSyncJson(dispatch.OP_SEEK, { rid, offset, whence }); -} - -/** Seek a file ID to the given offset under mode given by `whence`. - * - * (async () => { - * const file = await Deno.open("/foo/bar.txt"); - * await Deno.seek(file.rid, 0, 0); - * })(); - */ -export async function seek( - rid: number, - offset: number, - whence: SeekMode -): Promise<void> { - await sendAsyncJson(dispatch.OP_SEEK, { rid, offset, whence }); -} - -/** Close the file ID. */ -export function close(rid: number): void { - sendSyncJson(dispatch.OP_CLOSE, { rid }); -} - -/** The Deno abstraction for reading and writing files. */ -export class File - implements - Reader, - SyncReader, - Writer, - SyncWriter, - Seeker, - SyncSeeker, - Closer { - constructor(readonly rid: number) {} - - write(p: Uint8Array): Promise<number> { - return write(this.rid, p); - } - - writeSync(p: Uint8Array): number { - return writeSync(this.rid, p); - } - - read(p: Uint8Array): Promise<number | EOF> { - return read(this.rid, p); - } - - readSync(p: Uint8Array): number | EOF { - return readSync(this.rid, p); - } - - seek(offset: number, whence: SeekMode): Promise<void> { - return seek(this.rid, offset, whence); - } - - seekSync(offset: number, whence: SeekMode): void { - return seekSync(this.rid, offset, whence); - } - - close(): void { - close(this.rid); - } -} - -/** An instance of `File` for stdin. */ -export const stdin = new File(0); -/** An instance of `File` for stdout. */ -export const stdout = new File(1); -/** An instance of `File` for stderr. */ -export const stderr = new File(2); - -export type OpenMode = - /** Read-only. Default. Starts at beginning of file. */ - | "r" - /** Read-write. Start at beginning of file. */ - | "r+" - /** Write-only. Opens and truncates existing file or creates new one for - * writing only. - */ - | "w" - /** Read-write. Opens and truncates existing file or creates new one for - * writing and reading. - */ - | "w+" - /** Write-only. Opens existing file or creates new one. Each write appends - * content to the end of file. - */ - | "a" - /** Read-write. Behaves like "a" and allows to read from file. */ - | "a+" - /** Write-only. Exclusive create - creates new file only if one doesn't exist - * already. - */ - | "x" - /** Read-write. Behaves like `x` and allows to read from file. */ - | "x+"; - -/** A factory function for creating instances of `File` associated with the - * supplied file name. - * @internal - */ -export function create(filename: string): Promise<File> { - return open(filename, "w+"); -} diff --git a/js/files_test.ts b/js/files_test.ts deleted file mode 100644 index 004cb662b..000000000 --- a/js/files_test.ts +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, testPerm, assert, assertEquals } from "./test_util.ts"; - -test(function filesStdioFileDescriptors(): void { - assertEquals(Deno.stdin.rid, 0); - assertEquals(Deno.stdout.rid, 1); - assertEquals(Deno.stderr.rid, 2); -}); - -testPerm({ read: true }, async function filesCopyToStdout(): Promise<void> { - const filename = "package.json"; - const file = await Deno.open(filename); - assert(file.rid > 2); - const bytesWritten = await Deno.copy(Deno.stdout, file); - const fileSize = Deno.statSync(filename).len; - assertEquals(bytesWritten, fileSize); - console.log("bytes written", bytesWritten); -}); - -testPerm({ read: true }, async function filesToAsyncIterator(): Promise<void> { - const filename = "tests/hello.txt"; - const file = await Deno.open(filename); - - let totalSize = 0; - for await (const buf of Deno.toAsyncIterator(file)) { - totalSize += buf.byteLength; - } - - assertEquals(totalSize, 12); -}); - -test(async function readerToAsyncIterator(): Promise<void> { - // ref: https://github.com/denoland/deno/issues/2330 - const encoder = new TextEncoder(); - - class TestReader implements Deno.Reader { - private offset = 0; - private buf = new Uint8Array(encoder.encode(this.s)); - - constructor(private readonly s: string) {} - - async read(p: Uint8Array): Promise<number | Deno.EOF> { - const n = Math.min(p.byteLength, this.buf.byteLength - this.offset); - p.set(this.buf.slice(this.offset, this.offset + n)); - this.offset += n; - - if (n === 0) { - return Deno.EOF; - } - - return n; - } - } - - const reader = new TestReader("hello world!"); - - let totalSize = 0; - for await (const buf of Deno.toAsyncIterator(reader)) { - totalSize += buf.byteLength; - } - - assertEquals(totalSize, 12); -}); - -testPerm({ write: false }, async function writePermFailure(): Promise<void> { - const filename = "tests/hello.txt"; - const writeModes: Deno.OpenMode[] = ["w", "a", "x"]; - for (const mode of writeModes) { - let err; - try { - await Deno.open(filename, mode); - } catch (e) { - err = e; - } - assert(!!err); - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); - } -}); - -testPerm({ read: false }, async function readPermFailure(): Promise<void> { - let caughtError = false; - try { - await Deno.open("package.json", "r"); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); - -testPerm({ write: true }, async function writeNullBufferFailure(): Promise< - void -> { - const tempDir = Deno.makeTempDirSync(); - const filename = tempDir + "hello.txt"; - const file = await Deno.open(filename, "w"); - - // writing null should throw an error - let err; - try { - await file.write(null); - } catch (e) { - err = e; - } - // TODO: Check error kind when dispatch_minimal pipes errors properly - assert(!!err); - - file.close(); - await Deno.remove(tempDir, { recursive: true }); -}); - -testPerm( - { write: true, read: true }, - async function readNullBufferFailure(): Promise<void> { - const tempDir = Deno.makeTempDirSync(); - const filename = tempDir + "hello.txt"; - const file = await Deno.open(filename, "w+"); - - // reading file into null buffer should throw an error - let err; - try { - await file.read(null); - } catch (e) { - err = e; - } - // TODO: Check error kind when dispatch_minimal pipes errors properly - assert(!!err); - - file.close(); - await Deno.remove(tempDir, { recursive: true }); - } -); - -testPerm( - { write: false, read: false }, - async function readWritePermFailure(): Promise<void> { - const filename = "tests/hello.txt"; - const writeModes: Deno.OpenMode[] = ["r+", "w+", "a+", "x+"]; - for (const mode of writeModes) { - let err; - try { - await Deno.open(filename, mode); - } catch (e) { - err = e; - } - assert(!!err); - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); - } - } -); - -testPerm({ read: true, write: true }, async function createFile(): Promise< - void -> { - const tempDir = await Deno.makeTempDir(); - const filename = tempDir + "/test.txt"; - const f = await Deno.open(filename, "w"); - let fileInfo = Deno.statSync(filename); - assert(fileInfo.isFile()); - assert(fileInfo.len === 0); - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - await f.write(data); - fileInfo = Deno.statSync(filename); - assert(fileInfo.len === 5); - f.close(); - - // TODO: test different modes - await Deno.remove(tempDir, { recursive: true }); -}); - -testPerm({ read: true, write: true }, async function openModeWrite(): Promise< - void -> { - const tempDir = Deno.makeTempDirSync(); - const encoder = new TextEncoder(); - const filename = tempDir + "hello.txt"; - const data = encoder.encode("Hello world!\n"); - - let file = await Deno.open(filename, "w"); - // assert file was created - let fileInfo = Deno.statSync(filename); - assert(fileInfo.isFile()); - assertEquals(fileInfo.len, 0); - // write some data - await file.write(data); - fileInfo = Deno.statSync(filename); - assertEquals(fileInfo.len, 13); - // assert we can't read from file - let thrown = false; - try { - const buf = new Uint8Array(20); - await file.read(buf); - } catch (e) { - thrown = true; - } finally { - assert(thrown, "'w' mode shouldn't allow to read file"); - } - file.close(); - // assert that existing file is truncated on open - file = await Deno.open(filename, "w"); - file.close(); - const fileSize = Deno.statSync(filename).len; - assertEquals(fileSize, 0); - await Deno.remove(tempDir, { recursive: true }); -}); - -testPerm( - { read: true, write: true }, - async function openModeWriteRead(): Promise<void> { - const tempDir = Deno.makeTempDirSync(); - const encoder = new TextEncoder(); - const filename = tempDir + "hello.txt"; - const data = encoder.encode("Hello world!\n"); - - const file = await Deno.open(filename, "w+"); - // assert file was created - let fileInfo = Deno.statSync(filename); - assert(fileInfo.isFile()); - assertEquals(fileInfo.len, 0); - // write some data - await file.write(data); - fileInfo = Deno.statSync(filename); - assertEquals(fileInfo.len, 13); - - const buf = new Uint8Array(20); - await file.seek(0, Deno.SeekMode.SEEK_START); - const result = await file.read(buf); - assertEquals(result, 13); - file.close(); - - await Deno.remove(tempDir, { recursive: true }); - } -); - -testPerm({ read: true }, async function seekStart(): Promise<void> { - const filename = "tests/hello.txt"; - const file = await Deno.open(filename); - // Deliberately move 1 step forward - await file.read(new Uint8Array(1)); // "H" - // Skipping "Hello " - await file.seek(6, Deno.SeekMode.SEEK_START); - const buf = new Uint8Array(6); - await file.read(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); -}); - -testPerm({ read: true }, function seekSyncStart(): void { - const filename = "tests/hello.txt"; - const file = Deno.openSync(filename); - // Deliberately move 1 step forward - file.readSync(new Uint8Array(1)); // "H" - // Skipping "Hello " - file.seekSync(6, Deno.SeekMode.SEEK_START); - const buf = new Uint8Array(6); - file.readSync(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); -}); - -testPerm({ read: true }, async function seekCurrent(): Promise<void> { - const filename = "tests/hello.txt"; - const file = await Deno.open(filename); - // Deliberately move 1 step forward - await file.read(new Uint8Array(1)); // "H" - // Skipping "ello " - await file.seek(5, Deno.SeekMode.SEEK_CURRENT); - const buf = new Uint8Array(6); - await file.read(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); -}); - -testPerm({ read: true }, function seekSyncCurrent(): void { - const filename = "tests/hello.txt"; - const file = Deno.openSync(filename); - // Deliberately move 1 step forward - file.readSync(new Uint8Array(1)); // "H" - // Skipping "ello " - file.seekSync(5, Deno.SeekMode.SEEK_CURRENT); - const buf = new Uint8Array(6); - file.readSync(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); -}); - -testPerm({ read: true }, async function seekEnd(): Promise<void> { - const filename = "tests/hello.txt"; - const file = await Deno.open(filename); - await file.seek(-6, Deno.SeekMode.SEEK_END); - const buf = new Uint8Array(6); - await file.read(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); -}); - -testPerm({ read: true }, function seekSyncEnd(): void { - const filename = "tests/hello.txt"; - const file = Deno.openSync(filename); - file.seekSync(-6, Deno.SeekMode.SEEK_END); - const buf = new Uint8Array(6); - file.readSync(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); -}); - -testPerm({ read: true }, async function seekMode(): Promise<void> { - const filename = "tests/hello.txt"; - const file = await Deno.open(filename); - let err; - try { - await file.seek(1, -1); - } catch (e) { - err = e; - } - assert(!!err); - assertEquals(err.kind, Deno.ErrorKind.InvalidSeekMode); - assertEquals(err.name, "InvalidSeekMode"); - - // We should still be able to read the file - // since it is still open. - const buf = new Uint8Array(1); - await file.read(buf); // "H" - assertEquals(new TextDecoder().decode(buf), "H"); -}); diff --git a/js/form_data.ts b/js/form_data.ts deleted file mode 100644 index 89efb3c00..000000000 --- a/js/form_data.ts +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as domTypes from "./dom_types.ts"; -import * as blob from "./blob.ts"; -import * as domFile from "./dom_file.ts"; -import { DomIterableMixin } from "./mixins/dom_iterable.ts"; -import { requiredArguments } from "./util.ts"; - -const dataSymbol = Symbol("data"); - -class FormDataBase { - private [dataSymbol]: Array<[string, domTypes.FormDataEntryValue]> = []; - - /** Appends a new value onto an existing key inside a `FormData` - * object, or adds the key if it does not already exist. - * - * formData.append('name', 'first'); - * formData.append('name', 'second'); - */ - append(name: string, value: string): void; - append(name: string, value: blob.DenoBlob, filename?: string): void; - append(name: string, value: string | blob.DenoBlob, filename?: string): void { - requiredArguments("FormData.append", arguments.length, 2); - name = String(name); - if (value instanceof blob.DenoBlob) { - const dfile = new domFile.DomFileImpl([value], filename || name); - this[dataSymbol].push([name, dfile]); - } else { - this[dataSymbol].push([name, String(value)]); - } - } - - /** Deletes a key/value pair from a `FormData` object. - * - * formData.delete('name'); - */ - delete(name: string): void { - requiredArguments("FormData.delete", arguments.length, 1); - name = String(name); - let i = 0; - while (i < this[dataSymbol].length) { - if (this[dataSymbol][i][0] === name) { - this[dataSymbol].splice(i, 1); - } else { - i++; - } - } - } - - /** Returns an array of all the values associated with a given key - * from within a `FormData`. - * - * formData.getAll('name'); - */ - getAll(name: string): domTypes.FormDataEntryValue[] { - requiredArguments("FormData.getAll", arguments.length, 1); - name = String(name); - const values = []; - for (const entry of this[dataSymbol]) { - if (entry[0] === name) { - values.push(entry[1]); - } - } - - return values; - } - - /** Returns the first value associated with a given key from within a - * `FormData` object. - * - * formData.get('name'); - */ - get(name: string): domTypes.FormDataEntryValue | null { - requiredArguments("FormData.get", arguments.length, 1); - name = String(name); - for (const entry of this[dataSymbol]) { - if (entry[0] === name) { - return entry[1]; - } - } - - return null; - } - - /** Returns a boolean stating whether a `FormData` object contains a - * certain key/value pair. - * - * formData.has('name'); - */ - has(name: string): boolean { - requiredArguments("FormData.has", arguments.length, 1); - name = String(name); - return this[dataSymbol].some((entry): boolean => entry[0] === name); - } - - /** Sets a new value for an existing key inside a `FormData` object, or - * adds the key/value if it does not already exist. - * ref: https://xhr.spec.whatwg.org/#dom-formdata-set - * - * formData.set('name', 'value'); - */ - set(name: string, value: string): void; - set(name: string, value: blob.DenoBlob, filename?: string): void; - set(name: string, value: string | blob.DenoBlob, filename?: string): void { - requiredArguments("FormData.set", arguments.length, 2); - name = String(name); - - // If there are any entries in the context object’s entry list whose name - // is name, replace the first such entry with entry and remove the others - let found = false; - let i = 0; - while (i < this[dataSymbol].length) { - if (this[dataSymbol][i][0] === name) { - if (!found) { - if (value instanceof blob.DenoBlob) { - const dfile = new domFile.DomFileImpl([value], filename || name); - this[dataSymbol][i][1] = dfile; - } else { - this[dataSymbol][i][1] = String(value); - } - found = true; - } else { - this[dataSymbol].splice(i, 1); - continue; - } - } - i++; - } - - // Otherwise, append entry to the context object’s entry list. - if (!found) { - if (value instanceof blob.DenoBlob) { - const dfile = new domFile.DomFileImpl([value], filename || name); - this[dataSymbol].push([name, dfile]); - } else { - this[dataSymbol].push([name, String(value)]); - } - } - } - - get [Symbol.toStringTag](): string { - return "FormData"; - } -} - -export class FormData extends DomIterableMixin< - string, - domTypes.FormDataEntryValue, - typeof FormDataBase ->(FormDataBase, dataSymbol) {} diff --git a/js/form_data_test.ts b/js/form_data_test.ts deleted file mode 100644 index fe8b6cf32..000000000 --- a/js/form_data_test.ts +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, assert, assertEquals } from "./test_util.ts"; - -test(function formDataHasCorrectNameProp(): void { - assertEquals(FormData.name, "FormData"); -}); - -test(function formDataParamsAppendSuccess(): void { - const formData = new FormData(); - formData.append("a", "true"); - assertEquals(formData.get("a"), "true"); -}); - -test(function formDataParamsDeleteSuccess(): void { - const formData = new FormData(); - formData.append("a", "true"); - formData.append("b", "false"); - assertEquals(formData.get("b"), "false"); - formData.delete("b"); - assertEquals(formData.get("a"), "true"); - assertEquals(formData.get("b"), null); -}); - -test(function formDataParamsGetAllSuccess(): void { - const formData = new FormData(); - formData.append("a", "true"); - formData.append("b", "false"); - formData.append("a", "null"); - assertEquals(formData.getAll("a"), ["true", "null"]); - assertEquals(formData.getAll("b"), ["false"]); - assertEquals(formData.getAll("c"), []); -}); - -test(function formDataParamsGetSuccess(): void { - const formData = new FormData(); - formData.append("a", "true"); - formData.append("b", "false"); - formData.append("a", "null"); - formData.append("d", undefined); - formData.append("e", null); - assertEquals(formData.get("a"), "true"); - assertEquals(formData.get("b"), "false"); - assertEquals(formData.get("c"), null); - assertEquals(formData.get("d"), "undefined"); - assertEquals(formData.get("e"), "null"); -}); - -test(function formDataParamsHasSuccess(): void { - const formData = new FormData(); - formData.append("a", "true"); - formData.append("b", "false"); - assert(formData.has("a")); - assert(formData.has("b")); - assert(!formData.has("c")); -}); - -test(function formDataParamsSetSuccess(): void { - const formData = new FormData(); - formData.append("a", "true"); - formData.append("b", "false"); - formData.append("a", "null"); - assertEquals(formData.getAll("a"), ["true", "null"]); - assertEquals(formData.getAll("b"), ["false"]); - formData.set("a", "false"); - assertEquals(formData.getAll("a"), ["false"]); - formData.set("d", undefined); - assertEquals(formData.get("d"), "undefined"); - formData.set("e", null); - assertEquals(formData.get("e"), "null"); -}); - -test(function formDataSetEmptyBlobSuccess(): void { - const formData = new FormData(); - formData.set("a", new Blob([]), "blank.txt"); - formData.get("a"); - /* TODO Fix this test. - assert(file instanceof File); - if (typeof file !== "string") { - assertEquals(file.name, "blank.txt"); - } - */ -}); - -test(function formDataParamsForEachSuccess(): void { - const init = [["a", "54"], ["b", "true"]]; - const formData = new FormData(); - for (const [name, value] of init) { - formData.append(name, value); - } - let callNum = 0; - formData.forEach( - (value, key, parent): void => { - assertEquals(formData, parent); - assertEquals(value, init[callNum][1]); - assertEquals(key, init[callNum][0]); - callNum++; - } - ); - assertEquals(callNum, init.length); -}); - -test(function formDataParamsArgumentsCheck(): void { - const methodRequireOneParam = ["delete", "getAll", "get", "has", "forEach"]; - - const methodRequireTwoParams = ["append", "set"]; - - methodRequireOneParam.forEach( - (method): void => { - const formData = new FormData(); - let hasThrown = 0; - let errMsg = ""; - try { - formData[method](); - hasThrown = 1; - } catch (err) { - errMsg = err.message; - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); - assertEquals( - errMsg, - `FormData.${method} requires at least 1 argument, but only 0 present` - ); - } - ); - - methodRequireTwoParams.forEach( - (method: string): void => { - const formData = new FormData(); - let hasThrown = 0; - let errMsg = ""; - - try { - formData[method](); - hasThrown = 1; - } catch (err) { - errMsg = err.message; - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); - assertEquals( - errMsg, - `FormData.${method} requires at least 2 arguments, but only 0 present` - ); - - hasThrown = 0; - errMsg = ""; - try { - formData[method]("foo"); - hasThrown = 1; - } catch (err) { - errMsg = err.message; - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); - assertEquals( - errMsg, - `FormData.${method} requires at least 2 arguments, but only 1 present` - ); - } - ); -}); - -test(function toStringShouldBeWebCompatibility(): void { - const formData = new FormData(); - assertEquals(formData.toString(), "[object FormData]"); -}); diff --git a/js/format_error.ts b/js/format_error.ts deleted file mode 100644 index 801da0d0b..000000000 --- a/js/format_error.ts +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as dispatch from "./dispatch.ts"; -import { sendSync } from "./dispatch_json.ts"; - -// TODO(bartlomieju): move to `repl.ts`? -export function formatError(errString: string): string { - const res = sendSync(dispatch.OP_FORMAT_ERROR, { error: errString }); - return res.error; -} diff --git a/js/get_random_values.ts b/js/get_random_values.ts deleted file mode 100644 index e54f34785..000000000 --- a/js/get_random_values.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as dispatch from "./dispatch.ts"; -import { sendSync } from "./dispatch_json.ts"; -import { assert } from "./util.ts"; - -/** Synchronously collects cryptographically secure random values. The - * underlying CSPRNG in use is Rust's `rand::rngs::ThreadRng`. - * - * const arr = new Uint8Array(32); - * crypto.getRandomValues(arr); - */ -export function getRandomValues< - T extends - | Int8Array - | Uint8Array - | Uint8ClampedArray - | Int16Array - | Uint16Array - | Int32Array - | Uint32Array ->(typedArray: T): T { - assert(typedArray !== null, "Input must not be null"); - assert(typedArray.length <= 65536, "Input must not be longer than 65536"); - const ui8 = new Uint8Array( - typedArray.buffer, - typedArray.byteOffset, - typedArray.byteLength - ); - sendSync(dispatch.OP_GET_RANDOM_VALUES, {}, ui8); - return typedArray; -} diff --git a/js/get_random_values_test.ts b/js/get_random_values_test.ts deleted file mode 100644 index 68c13d597..000000000 --- a/js/get_random_values_test.ts +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, assertNotEquals, assertStrictEq } from "./test_util.ts"; - -test(function getRandomValuesInt8Array(): void { - const arr = new Int8Array(32); - crypto.getRandomValues(arr); - assertNotEquals(arr, new Int8Array(32)); -}); - -test(function getRandomValuesUint8Array(): void { - const arr = new Uint8Array(32); - crypto.getRandomValues(arr); - assertNotEquals(arr, new Uint8Array(32)); -}); - -test(function getRandomValuesUint8ClampedArray(): void { - const arr = new Uint8ClampedArray(32); - crypto.getRandomValues(arr); - assertNotEquals(arr, new Uint8ClampedArray(32)); -}); - -test(function getRandomValuesInt16Array(): void { - const arr = new Int16Array(4); - crypto.getRandomValues(arr); - assertNotEquals(arr, new Int16Array(4)); -}); - -test(function getRandomValuesUint16Array(): void { - const arr = new Uint16Array(4); - crypto.getRandomValues(arr); - assertNotEquals(arr, new Uint16Array(4)); -}); - -test(function getRandomValuesInt32Array(): void { - const arr = new Int32Array(8); - crypto.getRandomValues(arr); - assertNotEquals(arr, new Int32Array(8)); -}); - -test(function getRandomValuesUint32Array(): void { - const arr = new Uint32Array(8); - crypto.getRandomValues(arr); - assertNotEquals(arr, new Uint32Array(8)); -}); - -test(function getRandomValuesReturnValue(): void { - const arr = new Uint32Array(8); - const rtn = crypto.getRandomValues(arr); - assertNotEquals(arr, new Uint32Array(8)); - assertStrictEq(rtn, arr); -}); diff --git a/js/globals.ts b/js/globals.ts deleted file mode 100644 index b734b8da3..000000000 --- a/js/globals.ts +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -// This is a "special" module, in that it define the global runtime scope of -// Deno, and therefore it defines a lot of the runtime environment that code -// is evaluated in. We use this file to automatically build the runtime type -// library. - -// Modules which will make up part of the global public API surface should be -// imported as namespaces, so when the runtime type library is generated they -// can be expressed as a namespace in the type library. -import { window } from "./window.ts"; -import * as blob from "./blob.ts"; -import * as consoleTypes from "./console.ts"; -import * as csprng from "./get_random_values.ts"; -import * as customEvent from "./custom_event.ts"; -import * as Deno from "./deno.ts"; -import * as domTypes from "./dom_types.ts"; -import * as domFile from "./dom_file.ts"; -import * as event from "./event.ts"; -import * as eventTarget from "./event_target.ts"; -import * as formData from "./form_data.ts"; -import * as fetchTypes from "./fetch.ts"; -import * as headers from "./headers.ts"; -import * as textEncoding from "./text_encoding.ts"; -import * as timers from "./timers.ts"; -import * as url from "./url.ts"; -import * as urlSearchParams from "./url_search_params.ts"; -import * as workers from "./workers.ts"; -import * as performanceUtil from "./performance.ts"; - -import * as request from "./request.ts"; - -// These imports are not exposed and therefore are fine to just import the -// symbols required. -import { core } from "./core.ts"; - -// During the build process, augmentations to the variable `window` in this -// file are tracked and created as part of default library that is built into -// Deno, we only need to declare the enough to compile Deno. -declare global { - interface CallSite { - getThis(): unknown; - getTypeName(): string; - getFunction(): Function; - getFunctionName(): string; - getMethodName(): string; - getFileName(): string; - getLineNumber(): number | null; - getColumnNumber(): number | null; - getEvalOrigin(): string | null; - isToplevel(): boolean; - isEval(): boolean; - isNative(): boolean; - isConstructor(): boolean; - isAsync(): boolean; - isPromiseAll(): boolean; - getPromiseIndex(): number | null; - } - - interface ErrorConstructor { - prepareStackTrace(error: Error, structuredStackTrace: CallSite[]): string; - } - - interface Object { - [consoleTypes.customInspect]?(): string; - } -} - -// A self reference to the global object. -window.window = window; - -// This is the Deno namespace, it is handled differently from other window -// properties when building the runtime type library, as the whole module -// is flattened into a single namespace. -window.Deno = Deno; - -// Globally available functions and object instances. -window.atob = textEncoding.atob; -window.btoa = textEncoding.btoa; -window.fetch = fetchTypes.fetch; -window.clearTimeout = timers.clearTimeout; -window.clearInterval = timers.clearInterval; -window.console = new consoleTypes.Console(core.print); -window.setTimeout = timers.setTimeout; -window.setInterval = timers.setInterval; -window.location = (undefined as unknown) as domTypes.Location; -window.onload = undefined as undefined | Function; -window.onunload = undefined as undefined | Function; -// The following Crypto interface implementation is not up to par with the -// standard https://www.w3.org/TR/WebCryptoAPI/#crypto-interface as it does not -// yet incorporate the SubtleCrypto interface as its "subtle" property. -window.crypto = (csprng as unknown) as Crypto; -// window.queueMicrotask added by hand to self-maintained lib.deno_runtime.d.ts - -// When creating the runtime type library, we use modifications to `window` to -// determine what is in the global namespace. When we put a class in the -// namespace, we also need its global instance type as well, otherwise users -// won't be able to refer to instances. -// We have to export the type aliases, so that TypeScript _knows_ they are -// being used, which it cannot statically determine within this module. -window.Blob = blob.DenoBlob; -export type Blob = domTypes.Blob; - -export type Body = domTypes.Body; - -window.File = domFile.DomFileImpl as domTypes.DomFileConstructor; -export type File = domTypes.DomFile; - -export type CustomEventInit = domTypes.CustomEventInit; -window.CustomEvent = customEvent.CustomEvent; -export type CustomEvent = domTypes.CustomEvent; -export type EventInit = domTypes.EventInit; -window.Event = event.Event; -export type Event = domTypes.Event; -export type EventListener = domTypes.EventListener; -window.EventTarget = eventTarget.EventTarget; -export type EventTarget = domTypes.EventTarget; -window.URL = url.URL; -export type URL = url.URL; -window.URLSearchParams = urlSearchParams.URLSearchParams; -export type URLSearchParams = domTypes.URLSearchParams; - -// Using the `as` keyword to use standard compliant interfaces as the Deno -// implementations contain some implementation details we wouldn't want to -// expose in the runtime type library. -window.Headers = headers.Headers as domTypes.HeadersConstructor; -export type Headers = domTypes.Headers; -window.FormData = formData.FormData as domTypes.FormDataConstructor; -export type FormData = domTypes.FormData; - -window.TextEncoder = textEncoding.TextEncoder; -export type TextEncoder = textEncoding.TextEncoder; -window.TextDecoder = textEncoding.TextDecoder; -export type TextDecoder = textEncoding.TextDecoder; - -window.Request = request.Request as domTypes.RequestConstructor; -export type Request = domTypes.Request; - -window.Response = fetchTypes.Response; -export type Response = domTypes.Response; - -window.performance = new performanceUtil.Performance(); - -// This variable functioning correctly depends on `declareAsLet` -// in //tools/ts_library_builder/main.ts -window.onmessage = workers.onmessage; - -window.workerMain = workers.workerMain; -window.workerClose = workers.workerClose; -window.postMessage = workers.postMessage; - -window.Worker = workers.WorkerImpl; -export type Worker = workers.Worker; - -window[domTypes.eventTargetHost] = null; -window[domTypes.eventTargetListeners] = {}; -window[domTypes.eventTargetMode] = ""; -window[domTypes.eventTargetNodeType] = 0; -window[eventTarget.eventTargetAssignedSlot] = false; -window[eventTarget.eventTargetHasActivationBehavior] = false; -window.addEventListener = eventTarget.EventTarget.prototype.addEventListener; -window.dispatchEvent = eventTarget.EventTarget.prototype.dispatchEvent; -window.removeEventListener = - eventTarget.EventTarget.prototype.removeEventListener; - -// Registers the handler for window.onload function. -window.addEventListener( - "load", - (e: domTypes.Event): void => { - const onload = window.onload; - if (typeof onload === "function") { - onload(e); - } - } -); -// Registers the handler for window.onunload function. -window.addEventListener( - "unload", - (e: domTypes.Event): void => { - const onunload = window.onunload; - if (typeof onunload === "function") { - onunload(e); - } - } -); - -// below are interfaces that are available in TypeScript but -// have different signatures -export interface ImportMeta { - url: string; - main: boolean; -} - -export interface Crypto { - readonly subtle: null; - getRandomValues: < - T extends - | Int8Array - | Uint8Array - | Uint8ClampedArray - | Int16Array - | Uint16Array - | Int32Array - | Uint32Array - >( - typedArray: T - ) => T; -} diff --git a/js/globals_test.ts b/js/globals_test.ts deleted file mode 100644 index d7c50c5b1..000000000 --- a/js/globals_test.ts +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, assert } from "./test_util.ts"; - -test(function globalThisExists(): void { - assert(globalThis != null); -}); - -test(function windowExists(): void { - assert(window != null); -}); - -test(function windowWindowExists(): void { - assert(window.window === window); -}); - -test(function globalThisEqualsWindow(): void { - assert(globalThis === window); -}); - -test(function DenoNamespaceExists(): void { - assert(Deno != null); -}); - -test(function DenoNamespaceEqualsWindowDeno(): void { - assert(Deno === window.Deno); -}); - -test(function DenoNamespaceIsFrozen(): void { - assert(Object.isFrozen(Deno)); -}); - -test(function webAssemblyExists(): void { - assert(typeof WebAssembly.compile === "function"); -}); - -test(function DenoNamespaceImmutable(): void { - const denoCopy = window.Deno; - try { - // @ts-ignore - Deno = 1; - } catch {} - assert(denoCopy === Deno); - try { - // @ts-ignore - window.Deno = 1; - } catch {} - assert(denoCopy === Deno); - try { - delete window.Deno; - } catch {} - assert(denoCopy === Deno); - - const { readFile } = Deno; - try { - // @ts-ignore - Deno.readFile = 1; - } catch {} - assert(readFile === Deno.readFile); - try { - delete window.Deno.readFile; - } catch {} - assert(readFile === Deno.readFile); - - // @ts-ignore - const { print } = Deno.core; - try { - // @ts-ignore - Deno.core.print = 1; - } catch {} - // @ts-ignore - assert(print === Deno.core.print); - try { - // @ts-ignore - delete Deno.core.print; - } catch {} - // @ts-ignore - assert(print === Deno.core.print); -}); - -test(async function windowQueueMicrotask(): Promise<void> { - let resolve1: () => void | undefined; - let resolve2: () => void | undefined; - let microtaskDone = false; - const p1 = new Promise( - (res): void => { - resolve1 = (): void => { - microtaskDone = true; - res(); - }; - } - ); - const p2 = new Promise( - (res): void => { - resolve2 = (): void => { - assert(microtaskDone); - res(); - }; - } - ); - window.queueMicrotask(resolve1!); - setTimeout(resolve2!, 0); - await p1; - await p2; -}); diff --git a/js/headers.ts b/js/headers.ts deleted file mode 100644 index dc0de54dd..000000000 --- a/js/headers.ts +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as domTypes from "./dom_types.ts"; -import { DomIterableMixin } from "./mixins/dom_iterable.ts"; -import { requiredArguments } from "./util.ts"; - -// From node-fetch -// Copyright (c) 2016 David Frank. MIT License. -const invalidTokenRegex = /[^\^_`a-zA-Z\-0-9!#$%&'*+.|~]/; -const invalidHeaderCharRegex = /[^\t\x20-\x7e\x80-\xff]/; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function isHeaders(value: any): value is domTypes.Headers { - // eslint-disable-next-line @typescript-eslint/no-use-before-define - return value instanceof Headers; -} - -const headerMap = Symbol("header map"); - -// ref: https://fetch.spec.whatwg.org/#dom-headers -class HeadersBase { - private [headerMap]: Map<string, string>; - // TODO: headerGuard? Investigate if it is needed - // node-fetch did not implement this but it is in the spec - - private _normalizeParams(name: string, value?: string): string[] { - name = String(name).toLowerCase(); - value = String(value).trim(); - return [name, value]; - } - - // The following name/value validations are copied from - // https://github.com/bitinn/node-fetch/blob/master/src/headers.js - // Copyright (c) 2016 David Frank. MIT License. - private _validateName(name: string): void { - if (invalidTokenRegex.test(name) || name === "") { - throw new TypeError(`${name} is not a legal HTTP header name`); - } - } - - private _validateValue(value: string): void { - if (invalidHeaderCharRegex.test(value)) { - throw new TypeError(`${value} is not a legal HTTP header value`); - } - } - - constructor(init?: domTypes.HeadersInit) { - if (init === null) { - throw new TypeError( - "Failed to construct 'Headers'; The provided value was not valid" - ); - } else if (isHeaders(init)) { - this[headerMap] = new Map(init); - } else { - this[headerMap] = new Map(); - if (Array.isArray(init)) { - for (const tuple of init) { - // If header does not contain exactly two items, - // then throw a TypeError. - // ref: https://fetch.spec.whatwg.org/#concept-headers-fill - requiredArguments( - "Headers.constructor tuple array argument", - tuple.length, - 2 - ); - - const [name, value] = this._normalizeParams(tuple[0], tuple[1]); - this._validateName(name); - this._validateValue(value); - const existingValue = this[headerMap].get(name); - this[headerMap].set( - name, - existingValue ? `${existingValue}, ${value}` : value - ); - } - } else if (init) { - const names = Object.keys(init); - for (const rawName of names) { - const rawValue = init[rawName]; - const [name, value] = this._normalizeParams(rawName, rawValue); - this._validateName(name); - this._validateValue(value); - this[headerMap].set(name, value); - } - } - } - } - - // ref: https://fetch.spec.whatwg.org/#concept-headers-append - append(name: string, value: string): void { - requiredArguments("Headers.append", arguments.length, 2); - const [newname, newvalue] = this._normalizeParams(name, value); - this._validateName(newname); - this._validateValue(newvalue); - const v = this[headerMap].get(newname); - const str = v ? `${v}, ${newvalue}` : newvalue; - this[headerMap].set(newname, str); - } - - delete(name: string): void { - requiredArguments("Headers.delete", arguments.length, 1); - const [newname] = this._normalizeParams(name); - this._validateName(newname); - this[headerMap].delete(newname); - } - - get(name: string): string | null { - requiredArguments("Headers.get", arguments.length, 1); - const [newname] = this._normalizeParams(name); - this._validateName(newname); - const value = this[headerMap].get(newname); - return value || null; - } - - has(name: string): boolean { - requiredArguments("Headers.has", arguments.length, 1); - const [newname] = this._normalizeParams(name); - this._validateName(newname); - return this[headerMap].has(newname); - } - - set(name: string, value: string): void { - requiredArguments("Headers.set", arguments.length, 2); - const [newname, newvalue] = this._normalizeParams(name, value); - this._validateName(newname); - this._validateValue(newvalue); - this[headerMap].set(newname, newvalue); - } - - get [Symbol.toStringTag](): string { - return "Headers"; - } -} - -// @internal -export class Headers extends DomIterableMixin< - string, - string, - typeof HeadersBase ->(HeadersBase, headerMap) {} diff --git a/js/headers_test.ts b/js/headers_test.ts deleted file mode 100644 index f08283c51..000000000 --- a/js/headers_test.ts +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, assert, assertEquals } from "./test_util.ts"; - -// Logic heavily copied from web-platform-tests, make -// sure pass mostly header basic test -// ref: https://github.com/web-platform-tests/wpt/blob/7c50c216081d6ea3c9afe553ee7b64534020a1b2/fetch/api/headers/headers-basic.html -test(function newHeaderTest(): void { - new Headers(); - new Headers(undefined); - new Headers({}); - try { - new Headers(null); - } catch (e) { - assertEquals( - e.message, - "Failed to construct 'Headers'; The provided value was not valid" - ); - } -}); - -const headerDict = { - name1: "value1", - name2: "value2", - name3: "value3", - name4: undefined, - "Content-Type": "value4" -}; -const headerSeq = []; -for (const name in headerDict) { - headerSeq.push([name, headerDict[name]]); -} - -test(function newHeaderWithSequence(): void { - const headers = new Headers(headerSeq); - for (const name in headerDict) { - assertEquals(headers.get(name), String(headerDict[name])); - } - assertEquals(headers.get("length"), null); -}); - -test(function newHeaderWithRecord(): void { - const headers = new Headers(headerDict); - for (const name in headerDict) { - assertEquals(headers.get(name), String(headerDict[name])); - } -}); - -test(function newHeaderWithHeadersInstance(): void { - const headers = new Headers(headerDict); - const headers2 = new Headers(headers); - for (const name in headerDict) { - assertEquals(headers2.get(name), String(headerDict[name])); - } -}); - -test(function headerAppendSuccess(): void { - const headers = new Headers(); - for (const name in headerDict) { - headers.append(name, headerDict[name]); - assertEquals(headers.get(name), String(headerDict[name])); - } -}); - -test(function headerSetSuccess(): void { - const headers = new Headers(); - for (const name in headerDict) { - headers.set(name, headerDict[name]); - assertEquals(headers.get(name), String(headerDict[name])); - } -}); - -test(function headerHasSuccess(): void { - const headers = new Headers(headerDict); - for (const name in headerDict) { - assert(headers.has(name), "headers has name " + name); - assert( - !headers.has("nameNotInHeaders"), - "headers do not have header: nameNotInHeaders" - ); - } -}); - -test(function headerDeleteSuccess(): void { - const headers = new Headers(headerDict); - for (const name in headerDict) { - assert(headers.has(name), "headers have a header: " + name); - headers.delete(name); - assert(!headers.has(name), "headers do not have anymore a header: " + name); - } -}); - -test(function headerGetSuccess(): void { - const headers = new Headers(headerDict); - for (const name in headerDict) { - assertEquals(headers.get(name), String(headerDict[name])); - assertEquals(headers.get("nameNotInHeaders"), null); - } -}); - -test(function headerEntriesSuccess(): void { - const headers = new Headers(headerDict); - const iterators = headers.entries(); - for (const it of iterators) { - const key = it[0]; - const value = it[1]; - assert(headers.has(key)); - assertEquals(value, headers.get(key)); - } -}); - -test(function headerKeysSuccess(): void { - const headers = new Headers(headerDict); - const iterators = headers.keys(); - for (const it of iterators) { - assert(headers.has(it)); - } -}); - -test(function headerValuesSuccess(): void { - const headers = new Headers(headerDict); - const iterators = headers.values(); - const entries = headers.entries(); - const values = []; - for (const pair of entries) { - values.push(pair[1]); - } - for (const it of iterators) { - assert(values.includes(it)); - } -}); - -const headerEntriesDict = { - name1: "value1", - Name2: "value2", - name: "value3", - "content-Type": "value4", - "Content-Typ": "value5", - "Content-Types": "value6" -}; - -test(function headerForEachSuccess(): void { - const headers = new Headers(headerEntriesDict); - const keys = Object.keys(headerEntriesDict); - keys.forEach( - (key): void => { - const value = headerEntriesDict[key]; - const newkey = key.toLowerCase(); - headerEntriesDict[newkey] = value; - } - ); - let callNum = 0; - headers.forEach( - (value, key, container): void => { - assertEquals(headers, container); - assertEquals(value, headerEntriesDict[key]); - callNum++; - } - ); - assertEquals(callNum, keys.length); -}); - -test(function headerSymbolIteratorSuccess(): void { - assert(Symbol.iterator in Headers.prototype); - const headers = new Headers(headerEntriesDict); - for (const header of headers) { - const key = header[0]; - const value = header[1]; - assert(headers.has(key)); - assertEquals(value, headers.get(key)); - } -}); - -test(function headerTypesAvailable(): void { - function newHeaders(): Headers { - return new Headers(); - } - const headers = newHeaders(); - assert(headers instanceof Headers); -}); - -// Modified from https://github.com/bitinn/node-fetch/blob/7d3293200a91ad52b5ca7962f9d6fd1c04983edb/test/test.js#L2001-L2014 -// Copyright (c) 2016 David Frank. MIT License. -test(function headerIllegalReject(): void { - let errorCount = 0; - try { - new Headers({ "He y": "ok" }); - } catch (e) { - errorCount++; - } - try { - new Headers({ "Hé-y": "ok" }); - } catch (e) { - errorCount++; - } - try { - new Headers({ "He-y": "ăk" }); - } catch (e) { - errorCount++; - } - const headers = new Headers(); - try { - headers.append("Hé-y", "ok"); - } catch (e) { - errorCount++; - } - try { - headers.delete("Hé-y"); - } catch (e) { - errorCount++; - } - try { - headers.get("Hé-y"); - } catch (e) { - errorCount++; - } - try { - headers.has("Hé-y"); - } catch (e) { - errorCount++; - } - try { - headers.set("Hé-y", "ok"); - } catch (e) { - errorCount++; - } - try { - headers.set("", "ok"); - } catch (e) { - errorCount++; - } - assertEquals(errorCount, 9); - // 'o k' is valid value but invalid name - new Headers({ "He-y": "o k" }); -}); - -// If pair does not contain exactly two items,then throw a TypeError. -test(function headerParamsShouldThrowTypeError(): void { - let hasThrown = 0; - - try { - new Headers(([["1"]] as unknown) as Array<[string, string]>); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - - assertEquals(hasThrown, 2); -}); - -test(function headerParamsArgumentsCheck(): void { - const methodRequireOneParam = ["delete", "get", "has", "forEach"]; - - const methodRequireTwoParams = ["append", "set"]; - - methodRequireOneParam.forEach( - (method): void => { - const headers = new Headers(); - let hasThrown = 0; - let errMsg = ""; - try { - headers[method](); - hasThrown = 1; - } catch (err) { - errMsg = err.message; - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); - assertEquals( - errMsg, - `Headers.${method} requires at least 1 argument, but only 0 present` - ); - } - ); - - methodRequireTwoParams.forEach( - (method): void => { - const headers = new Headers(); - let hasThrown = 0; - let errMsg = ""; - - try { - headers[method](); - hasThrown = 1; - } catch (err) { - errMsg = err.message; - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); - assertEquals( - errMsg, - `Headers.${method} requires at least 2 arguments, but only 0 present` - ); - - hasThrown = 0; - errMsg = ""; - try { - headers[method]("foo"); - hasThrown = 1; - } catch (err) { - errMsg = err.message; - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); - assertEquals( - errMsg, - `Headers.${method} requires at least 2 arguments, but only 1 present` - ); - } - ); -}); - -test(function toStringShouldBeWebCompatibility(): void { - const headers = new Headers(); - assertEquals(headers.toString(), "[object Headers]"); -}); diff --git a/js/io.ts b/js/io.ts deleted file mode 100644 index 1a7bf8c4c..000000000 --- a/js/io.ts +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -// Interfaces 100% copied from Go. -// Documentation liberally lifted from them too. -// Thank you! We love Go! - -// TODO(kt3k): EOF should be `unique symbol` type. -// That might require some changes of ts_library_builder. -// See #2591 for more details. -export const EOF = null; -export type EOF = null; - -// Seek whence values. -// https://golang.org/pkg/io/#pkg-constants -export enum SeekMode { - SEEK_START = 0, - SEEK_CURRENT = 1, - SEEK_END = 2 -} - -// Reader is the interface that wraps the basic read() method. -// https://golang.org/pkg/io/#Reader -export interface Reader { - /** Reads up to p.byteLength bytes into `p`. It resolves to the number - * of bytes read (`0` < `n` <= `p.byteLength`) and rejects if any error encountered. - * Even if `read()` returns `n` < `p.byteLength`, it may use all of `p` as - * scratch space during the call. If some data is available but not - * `p.byteLength` bytes, `read()` conventionally returns what is available - * instead of waiting for more. - * - * When `read()` encounters end-of-file condition, it returns EOF symbol. - * - * When `read()` encounters an error, it rejects with an error. - * - * Callers should always process the `n` > `0` bytes returned before - * considering the EOF. Doing so correctly handles I/O errors that happen - * after reading some bytes and also both of the allowed EOF behaviors. - * - * Implementations must not retain `p`. - */ - read(p: Uint8Array): Promise<number | EOF>; -} - -export interface SyncReader { - readSync(p: Uint8Array): number | EOF; -} - -// Writer is the interface that wraps the basic write() method. -// https://golang.org/pkg/io/#Writer -export interface Writer { - /** Writes `p.byteLength` bytes from `p` to the underlying data - * stream. It resolves to the number of bytes written from `p` (`0` <= `n` <= - * `p.byteLength`) and any error encountered that caused the write to stop - * early. `write()` must return a non-null error if it returns `n` < - * `p.byteLength`. write() must not modify the slice data, even temporarily. - * - * Implementations must not retain `p`. - */ - write(p: Uint8Array): Promise<number>; -} - -export interface SyncWriter { - writeSync(p: Uint8Array): number; -} -// https://golang.org/pkg/io/#Closer -export interface Closer { - // The behavior of Close after the first call is undefined. Specific - // implementations may document their own behavior. - close(): void; -} - -// https://golang.org/pkg/io/#Seeker -export interface Seeker { - /** Seek sets the offset for the next `read()` or `write()` to offset, - * interpreted according to `whence`: `SeekStart` means relative to the start - * of the file, `SeekCurrent` means relative to the current offset, and - * `SeekEnd` means relative to the end. Seek returns the new offset relative - * to the start of the file and an error, if any. - * - * Seeking to an offset before the start of the file is an error. Seeking to - * any positive offset is legal, but the behavior of subsequent I/O operations - * on the underlying object is implementation-dependent. - */ - seek(offset: number, whence: SeekMode): Promise<void>; -} - -export interface SyncSeeker { - seekSync(offset: number, whence: SeekMode): void; -} - -// https://golang.org/pkg/io/#ReadCloser -export interface ReadCloser extends Reader, Closer {} - -// https://golang.org/pkg/io/#WriteCloser -export interface WriteCloser extends Writer, Closer {} - -// https://golang.org/pkg/io/#ReadSeeker -export interface ReadSeeker extends Reader, Seeker {} - -// https://golang.org/pkg/io/#WriteSeeker -export interface WriteSeeker extends Writer, Seeker {} - -// https://golang.org/pkg/io/#ReadWriteCloser -export interface ReadWriteCloser extends Reader, Writer, Closer {} - -// https://golang.org/pkg/io/#ReadWriteSeeker -export interface ReadWriteSeeker extends Reader, Writer, Seeker {} - -/** Copies from `src` to `dst` until either `EOF` is reached on `src` - * or an error occurs. It returns the number of bytes copied and the first - * error encountered while copying, if any. - * - * Because `copy()` is defined to read from `src` until `EOF`, it does not - * treat an `EOF` from `read()` as an error to be reported. - */ -// https://golang.org/pkg/io/#Copy -export async function copy(dst: Writer, src: Reader): Promise<number> { - let n = 0; - const b = new Uint8Array(32 * 1024); - let gotEOF = false; - while (gotEOF === false) { - const result = await src.read(b); - if (result === EOF) { - gotEOF = true; - } else { - n += await dst.write(b.subarray(0, result)); - } - } - return n; -} - -/** Turns `r` into async iterator. - * - * for await (const chunk of toAsyncIterator(reader)) { - * console.log(chunk) - * } - */ -export function toAsyncIterator(r: Reader): AsyncIterableIterator<Uint8Array> { - const b = new Uint8Array(1024); - // Keep track if end-of-file has been reached, then - // signal that iterator is done during subsequent next() - // call. This is required because `r` can return a `number | EOF` - // with data read and EOF reached. But if iterator returns - // `done` then `value` is discarded. - // - // See https://github.com/denoland/deno/issues/2330 for reference. - let sawEof = false; - - return { - [Symbol.asyncIterator](): AsyncIterableIterator<Uint8Array> { - return this; - }, - - async next(): Promise<IteratorResult<Uint8Array>> { - if (sawEof) { - return { value: new Uint8Array(), done: true }; - } - - const result = await r.read(b); - if (result === EOF) { - sawEof = true; - return { value: new Uint8Array(), done: true }; - } - - return { - value: b.subarray(0, result), - done: false - }; - } - }; -} diff --git a/js/lib.deno_runtime.d.ts b/js/lib.deno_runtime.d.ts deleted file mode 100644 index 94b6b61cd..000000000 --- a/js/lib.deno_runtime.d.ts +++ /dev/null @@ -1,2800 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. - -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-empty-interface */ - -/// <reference no-default-lib="true" /> -/// <reference lib="esnext" /> - -declare namespace Deno { - // @url js/os.d.ts - - /** The current process id of the runtime. */ - export let pid: number; - /** Reflects the NO_COLOR environment variable: https://no-color.org/ */ - export let noColor: boolean; - /** Check if running in terminal. - * - * console.log(Deno.isTTY().stdout); - */ - export function isTTY(): { - stdin: boolean; - stdout: boolean; - stderr: boolean; - }; - /** Get the hostname. - * Requires the `--allow-env` flag. - * - * console.log(Deno.hostname()); - */ - export function hostname(): string; - /** Exit the Deno process with optional exit code. */ - export function exit(code?: number): never; - /** Returns a snapshot of the environment variables at invocation. Mutating a - * property in the object will set that variable in the environment for - * the process. The environment object will only accept `string`s - * as values. - * - * const myEnv = Deno.env(); - * console.log(myEnv.SHELL); - * myEnv.TEST_VAR = "HELLO"; - * const newEnv = Deno.env(); - * console.log(myEnv.TEST_VAR == newEnv.TEST_VAR); - */ - export function env(): { - [index: string]: string; - }; - /** Returns the value of an environment variable at invocation. - * If the variable is not present, `undefined` will be returned. - * - * const myEnv = Deno.env(); - * console.log(myEnv.SHELL); - * myEnv.TEST_VAR = "HELLO"; - * const newEnv = Deno.env(); - * console.log(myEnv.TEST_VAR == newEnv.TEST_VAR); - */ - export function env(key: string): string | undefined; - /** - * Returns the current user's home directory. - * Requires the `--allow-env` flag. - */ - export function homeDir(): string; - /** - * Returns the path to the current deno executable. - * Requires the `--allow-env` flag. - */ - export function execPath(): string; - - // @url js/dir.d.ts - - /** - * `cwd()` Return a string representing the current working directory. - * If the current directory can be reached via multiple paths - * (due to symbolic links), `cwd()` may return - * any one of them. - * throws `NotFound` exception if directory not available - */ - export function cwd(): string; - /** - * `chdir()` Change the current working directory to path. - * throws `NotFound` exception if directory not available - */ - export function chdir(directory: string): void; - - // @url js/io.d.ts - - export const EOF: null; - export type EOF = null; - export enum SeekMode { - SEEK_START = 0, - SEEK_CURRENT = 1, - SEEK_END = 2 - } - export interface Reader { - /** Reads up to p.byteLength bytes into `p`. It resolves to the number - * of bytes read (`0` < `n` <= `p.byteLength`) and rejects if any error encountered. - * Even if `read()` returns `n` < `p.byteLength`, it may use all of `p` as - * scratch space during the call. If some data is available but not - * `p.byteLength` bytes, `read()` conventionally returns what is available - * instead of waiting for more. - * - * When `read()` encounters end-of-file condition, it returns EOF symbol. - * - * When `read()` encounters an error, it rejects with an error. - * - * Callers should always process the `n` > `0` bytes returned before - * considering the EOF. Doing so correctly handles I/O errors that happen - * after reading some bytes and also both of the allowed EOF behaviors. - * - * Implementations must not retain `p`. - */ - read(p: Uint8Array): Promise<number | EOF>; - } - export interface SyncReader { - readSync(p: Uint8Array): number | EOF; - } - export interface Writer { - /** Writes `p.byteLength` bytes from `p` to the underlying data - * stream. It resolves to the number of bytes written from `p` (`0` <= `n` <= - * `p.byteLength`) and any error encountered that caused the write to stop - * early. `write()` must return a non-null error if it returns `n` < - * `p.byteLength`. write() must not modify the slice data, even temporarily. - * - * Implementations must not retain `p`. - */ - write(p: Uint8Array): Promise<number>; - } - export interface SyncWriter { - writeSync(p: Uint8Array): number; - } - export interface Closer { - close(): void; - } - export interface Seeker { - /** Seek sets the offset for the next `read()` or `write()` to offset, - * interpreted according to `whence`: `SeekStart` means relative to the start - * of the file, `SeekCurrent` means relative to the current offset, and - * `SeekEnd` means relative to the end. Seek returns the new offset relative - * to the start of the file and an error, if any. - * - * Seeking to an offset before the start of the file is an error. Seeking to - * any positive offset is legal, but the behavior of subsequent I/O operations - * on the underlying object is implementation-dependent. - */ - seek(offset: number, whence: SeekMode): Promise<void>; - } - export interface SyncSeeker { - seekSync(offset: number, whence: SeekMode): void; - } - export interface ReadCloser extends Reader, Closer {} - export interface WriteCloser extends Writer, Closer {} - export interface ReadSeeker extends Reader, Seeker {} - export interface WriteSeeker extends Writer, Seeker {} - export interface ReadWriteCloser extends Reader, Writer, Closer {} - export interface ReadWriteSeeker extends Reader, Writer, Seeker {} - /** Copies from `src` to `dst` until either `EOF` is reached on `src` - * or an error occurs. It returns the number of bytes copied and the first - * error encountered while copying, if any. - * - * Because `copy()` is defined to read from `src` until `EOF`, it does not - * treat an `EOF` from `read()` as an error to be reported. - */ - export function copy(dst: Writer, src: Reader): Promise<number>; - /** Turns `r` into async iterator. - * - * for await (const chunk of toAsyncIterator(reader)) { - * console.log(chunk) - * } - */ - export function toAsyncIterator(r: Reader): AsyncIterableIterator<Uint8Array>; - - // @url js/files.d.ts - - /** Open a file and return an instance of the `File` object - * synchronously. - * - * const file = Deno.openSync("/foo/bar.txt"); - */ - export function openSync(filename: string, mode?: OpenMode): File; - /** Open a file and return an instance of the `File` object. - * - * (async () => { - * const file = await Deno.open("/foo/bar.txt"); - * })(); - */ - export function open(filename: string, mode?: OpenMode): Promise<File>; - /** Read synchronously from a file ID into an array buffer. - * - * Return `number | EOF` for the operation. - * - * const file = Deno.openSync("/foo/bar.txt"); - * const buf = new Uint8Array(100); - * const nread = Deno.readSync(file.rid, buf); - * const text = new TextDecoder().decode(buf); - * - */ - export function readSync(rid: number, p: Uint8Array): number | EOF; - /** Read from a file ID into an array buffer. - * - * Resolves with the `number | EOF` for the operation. - * - * (async () => { - * const file = await Deno.open("/foo/bar.txt"); - * const buf = new Uint8Array(100); - * const nread = await Deno.read(file.rid, buf); - * const text = new TextDecoder().decode(buf); - * })(); - */ - export function read(rid: number, p: Uint8Array): Promise<number | EOF>; - /** Write synchronously to the file ID the contents of the array buffer. - * - * Resolves with the number of bytes written. - * - * const encoder = new TextEncoder(); - * const data = encoder.encode("Hello world\n"); - * const file = Deno.openSync("/foo/bar.txt"); - * Deno.writeSync(file.rid, data); - */ - export function writeSync(rid: number, p: Uint8Array): number; - /** Write to the file ID the contents of the array buffer. - * - * Resolves with the number of bytes written. - * - * (async () => { - * const encoder = new TextEncoder(); - * const data = encoder.encode("Hello world\n"); - * const file = await Deno.open("/foo/bar.txt"); - * await Deno.write(file.rid, data); - * })(); - * - */ - export function write(rid: number, p: Uint8Array): Promise<number>; - /** Seek a file ID synchronously to the given offset under mode given by `whence`. - * - * const file = Deno.openSync("/foo/bar.txt"); - * Deno.seekSync(file.rid, 0, 0); - */ - export function seekSync(rid: number, offset: number, whence: SeekMode): void; - /** Seek a file ID to the given offset under mode given by `whence`. - * - * (async () => { - * const file = await Deno.open("/foo/bar.txt"); - * await Deno.seek(file.rid, 0, 0); - * })(); - */ - export function seek( - rid: number, - offset: number, - whence: SeekMode - ): Promise<void>; - /** Close the file ID. */ - export function close(rid: number): void; - /** The Deno abstraction for reading and writing files. */ - export class File - implements - Reader, - SyncReader, - Writer, - SyncWriter, - Seeker, - SyncSeeker, - Closer { - readonly rid: number; - constructor(rid: number); - write(p: Uint8Array): Promise<number>; - writeSync(p: Uint8Array): number; - read(p: Uint8Array): Promise<number | EOF>; - readSync(p: Uint8Array): number | EOF; - seek(offset: number, whence: SeekMode): Promise<void>; - seekSync(offset: number, whence: SeekMode): void; - close(): void; - } - /** An instance of `File` for stdin. */ - export const stdin: File; - /** An instance of `File` for stdout. */ - export const stdout: File; - /** An instance of `File` for stderr. */ - export const stderr: File; - export type OpenMode = - | "r" - /** Read-write. Start at beginning of file. */ - | "r+" - /** Write-only. Opens and truncates existing file or creates new one for - * writing only. - */ - | "w" - /** Read-write. Opens and truncates existing file or creates new one for - * writing and reading. - */ - | "w+" - /** Write-only. Opens existing file or creates new one. Each write appends - * content to the end of file. - */ - | "a" - /** Read-write. Behaves like "a" and allows to read from file. */ - | "a+" - /** Write-only. Exclusive create - creates new file only if one doesn't exist - * already. - */ - | "x" - /** Read-write. Behaves like `x` and allows to read from file. */ - | "x+"; - - // @url js/buffer.d.ts - - /** A Buffer is a variable-sized buffer of bytes with read() and write() - * methods. Based on https://golang.org/pkg/bytes/#Buffer - */ - export class Buffer implements Reader, SyncReader, Writer, SyncWriter { - private buf; - private off; - constructor(ab?: ArrayBuffer); - /** bytes() returns a slice holding the unread portion of the buffer. - * The slice is valid for use only until the next buffer modification (that - * is, only until the next call to a method like read(), write(), reset(), or - * truncate()). The slice aliases the buffer content at least until the next - * buffer modification, so immediate changes to the slice will affect the - * result of future reads. - */ - bytes(): Uint8Array; - /** toString() returns the contents of the unread portion of the buffer - * as a string. Warning - if multibyte characters are present when data is - * flowing through the buffer, this method may result in incorrect strings - * due to a character being split. - */ - toString(): string; - /** empty() returns whether the unread portion of the buffer is empty. */ - empty(): boolean; - /** length is a getter that returns the number of bytes of the unread - * portion of the buffer - */ - readonly length: number; - /** Returns the capacity of the buffer's underlying byte slice, that is, - * the total space allocated for the buffer's data. - */ - readonly capacity: number; - /** truncate() discards all but the first n unread bytes from the buffer but - * continues to use the same allocated storage. It throws if n is negative or - * greater than the length of the buffer. - */ - truncate(n: number): void; - /** reset() resets the buffer to be empty, but it retains the underlying - * storage for use by future writes. reset() is the same as truncate(0) - */ - reset(): void; - /** _tryGrowByReslice() is a version of grow for the fast-case - * where the internal buffer only needs to be resliced. It returns the index - * where bytes should be written and whether it succeeded. - * It returns -1 if a reslice was not needed. - */ - private _tryGrowByReslice; - private _reslice; - /** readSync() reads the next len(p) bytes from the buffer or until the buffer - * is drained. The return value n is the number of bytes read. If the - * buffer has no data to return, eof in the response will be true. - */ - readSync(p: Uint8Array): number | EOF; - read(p: Uint8Array): Promise<number | EOF>; - writeSync(p: Uint8Array): number; - write(p: Uint8Array): Promise<number>; - /** _grow() grows the buffer to guarantee space for n more bytes. - * It returns the index where bytes should be written. - * If the buffer can't grow it will throw with ErrTooLarge. - */ - private _grow; - /** grow() grows the buffer's capacity, if necessary, to guarantee space for - * another n bytes. After grow(n), at least n bytes can be written to the - * buffer without another allocation. If n is negative, grow() will panic. If - * the buffer can't grow it will throw ErrTooLarge. - * Based on https://golang.org/pkg/bytes/#Buffer.Grow - */ - grow(n: number): void; - /** readFrom() reads data from r until EOF and appends it to the buffer, - * growing the buffer as needed. It returns the number of bytes read. If the - * buffer becomes too large, readFrom will panic with ErrTooLarge. - * Based on https://golang.org/pkg/bytes/#Buffer.ReadFrom - */ - readFrom(r: Reader): Promise<number>; - /** Sync version of `readFrom` - */ - readFromSync(r: SyncReader): number; - } - /** Read `r` until EOF and return the content as `Uint8Array`. - */ - export function readAll(r: Reader): Promise<Uint8Array>; - /** Read synchronously `r` until EOF and return the content as `Uint8Array`. - */ - export function readAllSync(r: SyncReader): Uint8Array; - /** Write all the content of `arr` to `w`. - */ - export function writeAll(w: Writer, arr: Uint8Array): Promise<void>; - /** Write synchronously all the content of `arr` to `w`. - */ - export function writeAllSync(w: SyncWriter, arr: Uint8Array): void; - - // @url js/mkdir.d.ts - - /** Creates a new directory with the specified path synchronously. - * If `recursive` is set to true, nested directories will be created (also known - * as "mkdir -p"). - * `mode` sets permission bits (before umask) on UNIX and does nothing on - * Windows. - * - * Deno.mkdirSync("new_dir"); - * Deno.mkdirSync("nested/directories", true); - */ - export function mkdirSync( - path: string, - recursive?: boolean, - mode?: number - ): void; - /** Creates a new directory with the specified path. - * If `recursive` is set to true, nested directories will be created (also known - * as "mkdir -p"). - * `mode` sets permission bits (before umask) on UNIX and does nothing on - * Windows. - * - * await Deno.mkdir("new_dir"); - * await Deno.mkdir("nested/directories", true); - */ - export function mkdir( - path: string, - recursive?: boolean, - mode?: number - ): Promise<void>; - - // @url js/make_temp_dir.d.ts - - export interface MakeTempDirOptions { - dir?: string; - prefix?: string; - suffix?: string; - } - /** makeTempDirSync is the synchronous version of `makeTempDir`. - * - * const tempDirName0 = Deno.makeTempDirSync(); - * const tempDirName1 = Deno.makeTempDirSync({ prefix: 'my_temp' }); - */ - export function makeTempDirSync(options?: MakeTempDirOptions): string; - /** makeTempDir creates a new temporary directory in the directory `dir`, its - * name beginning with `prefix` and ending with `suffix`. - * It returns the full path to the newly created directory. - * If `dir` is unspecified, tempDir uses the default directory for temporary - * files. Multiple programs calling tempDir simultaneously will not choose the - * same directory. It is the caller's responsibility to remove the directory - * when no longer needed. - * - * const tempDirName0 = await Deno.makeTempDir(); - * const tempDirName1 = await Deno.makeTempDir({ prefix: 'my_temp' }); - */ - export function makeTempDir(options?: MakeTempDirOptions): Promise<string>; - - // @url js/chmod.d.ts - - /** Changes the permission of a specific file/directory of specified path - * synchronously. - * - * Deno.chmodSync("/path/to/file", 0o666); - */ - export function chmodSync(path: string, mode: number): void; - /** Changes the permission of a specific file/directory of specified path. - * - * await Deno.chmod("/path/to/file", 0o666); - */ - export function chmod(path: string, mode: number): Promise<void>; - - // @url js/chown.d.ts - - /** - * Change owner of a regular file or directory synchronously. Unix only at the moment. - * @param path path to the file - * @param uid user id of the new owner - * @param gid group id of the new owner - */ - export function chownSync(path: string, uid: number, gid: number): void; - /** - * Change owner of a regular file or directory asynchronously. Unix only at the moment. - * @param path path to the file - * @param uid user id of the new owner - * @param gid group id of the new owner - */ - export function chown(path: string, uid: number, gid: number): Promise<void>; - - // @url js/utime.d.ts - - /** Synchronously changes the access and modification times of a file system - * object referenced by `filename`. Given times are either in seconds - * (Unix epoch time) or as `Date` objects. - * - * Deno.utimeSync("myfile.txt", 1556495550, new Date()); - */ - export function utimeSync( - filename: string, - atime: number | Date, - mtime: number | Date - ): void; - /** Changes the access and modification times of a file system object - * referenced by `filename`. Given times are either in seconds - * (Unix epoch time) or as `Date` objects. - * - * await Deno.utime("myfile.txt", 1556495550, new Date()); - */ - export function utime( - filename: string, - atime: number | Date, - mtime: number | Date - ): Promise<void>; - - // @url js/remove.d.ts - - export interface RemoveOption { - recursive?: boolean; - } - /** Removes the named file or directory synchronously. Would throw - * error if permission denied, not found, or directory not empty if `recursive` - * set to false. - * `recursive` is set to false by default. - * - * Deno.removeSync("/path/to/dir/or/file", {recursive: false}); - */ - export function removeSync(path: string, options?: RemoveOption): void; - /** Removes the named file or directory. Would throw error if - * permission denied, not found, or directory not empty if `recursive` set - * to false. - * `recursive` is set to false by default. - * - * await Deno.remove("/path/to/dir/or/file", {recursive: false}); - */ - export function remove(path: string, options?: RemoveOption): Promise<void>; - - // @url js/rename.d.ts - - /** Synchronously renames (moves) `oldpath` to `newpath`. If `newpath` already - * exists and is not a directory, `renameSync()` replaces it. OS-specific - * restrictions may apply when `oldpath` and `newpath` are in different - * directories. - * - * Deno.renameSync("old/path", "new/path"); - */ - export function renameSync(oldpath: string, newpath: string): void; - /** Renames (moves) `oldpath` to `newpath`. If `newpath` already exists and is - * not a directory, `rename()` replaces it. OS-specific restrictions may apply - * when `oldpath` and `newpath` are in different directories. - * - * await Deno.rename("old/path", "new/path"); - */ - export function rename(oldpath: string, newpath: string): Promise<void>; - - // @url js/read_file.d.ts - - /** Read the entire contents of a file synchronously. - * - * const decoder = new TextDecoder("utf-8"); - * const data = Deno.readFileSync("hello.txt"); - * console.log(decoder.decode(data)); - */ - export function readFileSync(filename: string): Uint8Array; - /** Read the entire contents of a file. - * - * const decoder = new TextDecoder("utf-8"); - * const data = await Deno.readFile("hello.txt"); - * console.log(decoder.decode(data)); - */ - export function readFile(filename: string): Promise<Uint8Array>; - - // @url js/file_info.d.ts - - /** A FileInfo describes a file and is returned by `stat`, `lstat`, - * `statSync`, `lstatSync`. - */ - export interface FileInfo { - /** The size of the file, in bytes. */ - len: number; - /** The last modification time of the file. This corresponds to the `mtime` - * field from `stat` on Unix and `ftLastWriteTime` on Windows. This may not - * be available on all platforms. - */ - modified: number | null; - /** The last access time of the file. This corresponds to the `atime` - * field from `stat` on Unix and `ftLastAccessTime` on Windows. This may not - * be available on all platforms. - */ - accessed: number | null; - /** The last access time of the file. This corresponds to the `birthtime` - * field from `stat` on Unix and `ftCreationTime` on Windows. This may not - * be available on all platforms. - */ - created: number | null; - /** The underlying raw st_mode bits that contain the standard Unix permissions - * for this file/directory. TODO Match behavior with Go on windows for mode. - */ - mode: number | null; - /** The file or directory name. */ - name: string | null; - /** Returns whether this is info for a regular file. This result is mutually - * exclusive to `FileInfo.isDirectory` and `FileInfo.isSymlink`. - */ - isFile(): boolean; - /** Returns whether this is info for a regular directory. This result is - * mutually exclusive to `FileInfo.isFile` and `FileInfo.isSymlink`. - */ - isDirectory(): boolean; - /** Returns whether this is info for a symlink. This result is - * mutually exclusive to `FileInfo.isFile` and `FileInfo.isDirectory`. - */ - isSymlink(): boolean; - } - - // @url js/read_dir.d.ts - - /** Reads the directory given by path and returns a list of file info - * synchronously. - * - * const files = Deno.readDirSync("/"); - */ - export function readDirSync(path: string): FileInfo[]; - /** Reads the directory given by path and returns a list of file info. - * - * const files = await Deno.readDir("/"); - */ - export function readDir(path: string): Promise<FileInfo[]>; - - // @url js/copy_file.d.ts - - /** Copies the contents of a file to another by name synchronously. - * Creates a new file if target does not exists, and if target exists, - * overwrites original content of the target file. - * - * It would also copy the permission of the original file - * to the destination. - * - * Deno.copyFileSync("from.txt", "to.txt"); - */ - export function copyFileSync(from: string, to: string): void; - /** Copies the contents of a file to another by name. - * - * Creates a new file if target does not exists, and if target exists, - * overwrites original content of the target file. - * - * It would also copy the permission of the original file - * to the destination. - * - * await Deno.copyFile("from.txt", "to.txt"); - */ - export function copyFile(from: string, to: string): Promise<void>; - - // @url js/read_link.d.ts - - /** Returns the destination of the named symbolic link synchronously. - * - * const targetPath = Deno.readlinkSync("symlink/path"); - */ - export function readlinkSync(name: string): string; - /** Returns the destination of the named symbolic link. - * - * const targetPath = await Deno.readlink("symlink/path"); - */ - export function readlink(name: string): Promise<string>; - - // @url js/stat.d.ts - - interface StatResponse { - isFile: boolean; - isSymlink: boolean; - len: number; - modified: number; - accessed: number; - created: number; - mode: number; - hasMode: boolean; - name: string | null; - } - /** Queries the file system for information on the path provided. If the given - * path is a symlink information about the symlink will be returned. - * - * const fileInfo = await Deno.lstat("hello.txt"); - * assert(fileInfo.isFile()); - */ - export function lstat(filename: string): Promise<FileInfo>; - /** Queries the file system for information on the path provided synchronously. - * If the given path is a symlink information about the symlink will be - * returned. - * - * const fileInfo = Deno.lstatSync("hello.txt"); - * assert(fileInfo.isFile()); - */ - export function lstatSync(filename: string): FileInfo; - /** Queries the file system for information on the path provided. `stat` Will - * always follow symlinks. - * - * const fileInfo = await Deno.stat("hello.txt"); - * assert(fileInfo.isFile()); - */ - export function stat(filename: string): Promise<FileInfo>; - /** Queries the file system for information on the path provided synchronously. - * `statSync` Will always follow symlinks. - * - * const fileInfo = Deno.statSync("hello.txt"); - * assert(fileInfo.isFile()); - */ - export function statSync(filename: string): FileInfo; - - // @url js/link.d.ts - - /** Synchronously creates `newname` as a hard link to `oldname`. - * - * Deno.linkSync("old/name", "new/name"); - */ - export function linkSync(oldname: string, newname: string): void; - /** Creates `newname` as a hard link to `oldname`. - * - * await Deno.link("old/name", "new/name"); - */ - export function link(oldname: string, newname: string): Promise<void>; - - // @url js/symlink.d.ts - - /** Synchronously creates `newname` as a symbolic link to `oldname`. The type - * argument can be set to `dir` or `file` and is only available on Windows - * (ignored on other platforms). - * - * Deno.symlinkSync("old/name", "new/name"); - */ - export function symlinkSync( - oldname: string, - newname: string, - type?: string - ): void; - /** Creates `newname` as a symbolic link to `oldname`. The type argument can be - * set to `dir` or `file` and is only available on Windows (ignored on other - * platforms). - * - * await Deno.symlink("old/name", "new/name"); - */ - export function symlink( - oldname: string, - newname: string, - type?: string - ): Promise<void>; - - // @url js/write_file.d.ts - - /** Options for writing to a file. - * `perm` would change the file's permission if set. - * `create` decides if the file should be created if not exists (default: true) - * `append` decides if the file should be appended (default: false) - */ - export interface WriteFileOptions { - perm?: number; - create?: boolean; - append?: boolean; - } - /** Write a new file, with given filename and data synchronously. - * - * const encoder = new TextEncoder(); - * const data = encoder.encode("Hello world\n"); - * Deno.writeFileSync("hello.txt", data); - */ - export function writeFileSync( - filename: string, - data: Uint8Array, - options?: WriteFileOptions - ): void; - /** Write a new file, with given filename and data. - * - * const encoder = new TextEncoder(); - * const data = encoder.encode("Hello world\n"); - * await Deno.writeFile("hello.txt", data); - */ - export function writeFile( - filename: string, - data: Uint8Array, - options?: WriteFileOptions - ): Promise<void>; - - // @url js/error_stack.d.ts - - interface Location { - /** The full url for the module, e.g. `file://some/file.ts` or - * `https://some/file.ts`. */ - filename: string; - /** The line number in the file. It is assumed to be 1-indexed. */ - line: number; - /** The column number in the file. It is assumed to be 1-indexed. */ - column: number; - } - /** Given a current location in a module, lookup the source location and - * return it. - * - * When Deno transpiles code, it keep source maps of the transpiled code. This - * function can be used to lookup the original location. This is automatically - * done when accessing the `.stack` of an error, or when an uncaught error is - * logged. This function can be used to perform the lookup for creating better - * error handling. - * - * **Note:** `line` and `column` are 1 indexed, which matches display - * expectations, but is not typical of most index numbers in Deno. - * - * An example: - * - * const orig = Deno.applySourceMap({ - * location: "file://my/module.ts", - * line: 5, - * column: 15 - * }); - * console.log(`${orig.filename}:${orig.line}:${orig.column}`); - * - */ - export function applySourceMap(location: Location): Location; - - // @url js/errors.d.ts - - /** A Deno specific error. The `kind` property is set to a specific error code - * which can be used to in application logic. - * - * try { - * somethingThatMightThrow(); - * } catch (e) { - * if ( - * e instanceof Deno.DenoError && - * e.kind === Deno.ErrorKind.Overflow - * ) { - * console.error("Overflow error!"); - * } - * } - * - */ - export class DenoError<T extends ErrorKind> extends Error { - readonly kind: T; - constructor(kind: T, msg: string); - } - export enum ErrorKind { - NoError = 0, - NotFound = 1, - PermissionDenied = 2, - ConnectionRefused = 3, - ConnectionReset = 4, - ConnectionAborted = 5, - NotConnected = 6, - AddrInUse = 7, - AddrNotAvailable = 8, - BrokenPipe = 9, - AlreadyExists = 10, - WouldBlock = 11, - InvalidInput = 12, - InvalidData = 13, - TimedOut = 14, - Interrupted = 15, - WriteZero = 16, - Other = 17, - UnexpectedEof = 18, - BadResource = 19, - CommandFailed = 20, - EmptyHost = 21, - IdnaError = 22, - InvalidPort = 23, - InvalidIpv4Address = 24, - InvalidIpv6Address = 25, - InvalidDomainCharacter = 26, - RelativeUrlWithoutBase = 27, - RelativeUrlWithCannotBeABaseBase = 28, - SetHostOnCannotBeABaseUrl = 29, - Overflow = 30, - HttpUser = 31, - HttpClosed = 32, - HttpCanceled = 33, - HttpParse = 34, - HttpOther = 35, - TooLarge = 36, - InvalidUri = 37, - InvalidSeekMode = 38, - OpNotAvailable = 39, - WorkerInitFailed = 40, - UnixError = 41, - NoAsyncSupport = 42, - NoSyncSupport = 43, - ImportMapError = 44, - InvalidPath = 45, - ImportPrefixMissing = 46, - UnsupportedFetchScheme = 47, - TooManyRedirects = 48, - Diagnostic = 49, - JSError = 50 - } - - // @url js/permissions.d.ts - - /** Permissions as granted by the caller */ - export interface Permissions { - read: boolean; - write: boolean; - net: boolean; - env: boolean; - run: boolean; - hrtime: boolean; - } - export type Permission = keyof Permissions; - /** Inspect granted permissions for the current program. - * - * if (Deno.permissions().read) { - * const file = await Deno.readFile("example.test"); - * // ... - * } - */ - export function permissions(): Permissions; - /** Revoke a permission. When the permission was already revoked nothing changes - * - * if (Deno.permissions().read) { - * const file = await Deno.readFile("example.test"); - * Deno.revokePermission('read'); - * } - * Deno.readFile("example.test"); // -> error or permission prompt - */ - export function revokePermission(permission: Permission): void; - - // @url js/truncate.d.ts - - /** Truncates or extends the specified file synchronously, updating the size of - * this file to become size. - * - * Deno.truncateSync("hello.txt", 10); - */ - export function truncateSync(name: string, len?: number): void; - /** - * Truncates or extends the specified file, updating the size of this file to - * become size. - * - * await Deno.truncate("hello.txt", 10); - */ - export function truncate(name: string, len?: number): Promise<void>; - - // @url js/net.d.ts - - type Transport = "tcp"; - interface Addr { - transport: Transport; - address: string; - } - - /** A Listener is a generic network listener for stream-oriented protocols. */ - export interface Listener extends AsyncIterator<Conn> { - /** Waits for and resolves to the next connection to the `Listener`. */ - accept(): Promise<Conn>; - /** Close closes the listener. Any pending accept promises will be rejected - * with errors. - */ - close(): void; - /** Return the address of the `Listener`. */ - addr(): Addr; - [Symbol.asyncIterator](): AsyncIterator<Conn>; - } - export interface Conn extends Reader, Writer, Closer { - /** The local address of the connection. */ - localAddr: string; - /** The remote address of the connection. */ - remoteAddr: string; - /** The resource ID of the connection. */ - rid: number; - /** Shuts down (`shutdown(2)`) the reading side of the TCP connection. Most - * callers should just use `close()`. - */ - closeRead(): void; - /** Shuts down (`shutdown(2)`) the writing side of the TCP connection. Most - * callers should just use `close()`. - */ - closeWrite(): void; - } - - export interface ListenOptions { - port: number; - hostname?: string; - transport?: Transport; - } - - /** Listen announces on the local transport address. - * - * @param options - * @param options.port The port to connect to. (Required.) - * @param options.hostname A literal IP address or host name that can be - * resolved to an IP address. If not specified, defaults to 0.0.0.0 - * @param options.transport Defaults to "tcp". Later we plan to add "tcp4", - * "tcp6", "udp", "udp4", "udp6", "ip", "ip4", "ip6", "unix", "unixgram" and - * "unixpacket". - * - * Examples: - * - * listen({ port: 80 }) - * listen({ hostname: "192.0.2.1", port: 80 }) - * listen({ hostname: "[2001:db8::1]", port: 80 }); - * listen({ hostname: "golang.org", port: 80, transport: "tcp" }) - */ - export function listen(options: ListenOptions): Listener; - - export interface DialOptions { - port: number; - hostname?: string; - transport?: Transport; - } - - /** Dial connects to the address on the named transport. - * - * @param options - * @param options.port The port to connect to. (Required.) - * @param options.hostname A literal IP address or host name that can be - * resolved to an IP address. If not specified, defaults to 127.0.0.1 - * @param options.transport Defaults to "tcp". Later we plan to add "tcp4", - * "tcp6", "udp", "udp4", "udp6", "ip", "ip4", "ip6", "unix", "unixgram" and - * "unixpacket". - * - * Examples: - * - * dial({ port: 80 }) - * dial({ hostname: "192.0.2.1", port: 80 }) - * dial({ hostname: "[2001:db8::1]", port: 80 }); - * dial({ hostname: "golang.org", port: 80, transport: "tcp" }) - */ - export function dial(options: DialOptions): Promise<Conn>; - - export interface DialTLSOptions { - port: number; - hostname?: string; - } - - /** - * dialTLS establishes a secure connection over TLS (transport layer security). - */ - export function dialTLS(options: DialTLSOptions): Promise<Conn>; - - // @url js/metrics.d.ts - export interface Metrics { - opsDispatched: number; - opsCompleted: number; - bytesSentControl: number; - bytesSentData: number; - bytesReceived: number; - } - /** Receive metrics from the privileged side of Deno. - * - * > console.table(Deno.metrics()) - * ┌──────────────────┬────────┐ - * │ (index) │ Values │ - * ├──────────────────┼────────┤ - * │ opsDispatched │ 9 │ - * │ opsCompleted │ 9 │ - * │ bytesSentControl │ 504 │ - * │ bytesSentData │ 0 │ - * │ bytesReceived │ 856 │ - * └──────────────────┴────────┘ - */ - export function metrics(): Metrics; - - // @url js/resources.d.ts - - interface ResourceMap { - [rid: number]: string; - } - /** Returns a map of open _file like_ resource ids along with their string - * representation. - */ - export function resources(): ResourceMap; - - // @url js/process.d.ts - - /** How to handle subprocess stdio. - * - * "inherit" The default if unspecified. The child inherits from the - * corresponding parent descriptor. - * - * "piped" A new pipe should be arranged to connect the parent and child - * subprocesses. - * - * "null" This stream will be ignored. This is the equivalent of attaching the - * stream to /dev/null. - */ - type ProcessStdio = "inherit" | "piped" | "null"; - export interface RunOptions { - args: string[]; - cwd?: string; - env?: { - [key: string]: string; - }; - stdout?: ProcessStdio | number; - stderr?: ProcessStdio | number; - stdin?: ProcessStdio | number; - } - /** Send a signal to process under given PID. Unix only at this moment. - * If pid is negative, the signal will be sent to the process group identified - * by -pid. - * Requires the `--allow-run` flag. - */ - export function kill(pid: number, signo: number): void; - export class Process { - readonly rid: number; - readonly pid: number; - readonly stdin?: WriteCloser; - readonly stdout?: ReadCloser; - readonly stderr?: ReadCloser; - status(): Promise<ProcessStatus>; - /** Buffer the stdout and return it as Uint8Array after EOF. - * You must set stdout to "piped" when creating the process. - * This calls close() on stdout after its done. - */ - output(): Promise<Uint8Array>; - /** Buffer the stderr and return it as Uint8Array after EOF. - * You must set stderr to "piped" when creating the process. - * This calls close() on stderr after its done. - */ - stderrOutput(): Promise<Uint8Array>; - close(): void; - kill(signo: number): void; - } - export interface ProcessStatus { - success: boolean; - code?: number; - signal?: number; - } - /** - * Spawns new subprocess. - * - * Subprocess uses same working directory as parent process unless `opt.cwd` - * is specified. - * - * Environmental variables for subprocess can be specified using `opt.env` - * mapping. - * - * By default subprocess inherits stdio of parent process. To change that - * `opt.stdout`, `opt.stderr` and `opt.stdin` can be specified independently - - * they can be set to either `ProcessStdio` or `rid` of open file. - */ - export function run(opt: RunOptions): Process; - enum LinuxSignal { - SIGHUP = 1, - SIGINT = 2, - SIGQUIT = 3, - SIGILL = 4, - SIGTRAP = 5, - SIGABRT = 6, - SIGBUS = 7, - SIGFPE = 8, - SIGKILL = 9, - SIGUSR1 = 10, - SIGSEGV = 11, - SIGUSR2 = 12, - SIGPIPE = 13, - SIGALRM = 14, - SIGTERM = 15, - SIGSTKFLT = 16, - SIGCHLD = 17, - SIGCONT = 18, - SIGSTOP = 19, - SIGTSTP = 20, - SIGTTIN = 21, - SIGTTOU = 22, - SIGURG = 23, - SIGXCPU = 24, - SIGXFSZ = 25, - SIGVTALRM = 26, - SIGPROF = 27, - SIGWINCH = 28, - SIGIO = 29, - SIGPWR = 30, - SIGSYS = 31 - } - enum MacOSSignal { - SIGHUP = 1, - SIGINT = 2, - SIGQUIT = 3, - SIGILL = 4, - SIGTRAP = 5, - SIGABRT = 6, - SIGEMT = 7, - SIGFPE = 8, - SIGKILL = 9, - SIGBUS = 10, - SIGSEGV = 11, - SIGSYS = 12, - SIGPIPE = 13, - SIGALRM = 14, - SIGTERM = 15, - SIGURG = 16, - SIGSTOP = 17, - SIGTSTP = 18, - SIGCONT = 19, - SIGCHLD = 20, - SIGTTIN = 21, - SIGTTOU = 22, - SIGIO = 23, - SIGXCPU = 24, - SIGXFSZ = 25, - SIGVTALRM = 26, - SIGPROF = 27, - SIGWINCH = 28, - SIGINFO = 29, - SIGUSR1 = 30, - SIGUSR2 = 31 - } - /** Signals numbers. This is platform dependent. - */ - export const Signal: typeof MacOSSignal | typeof LinuxSignal; - export {}; - - // @url js/console.d.ts - - type ConsoleOptions = Partial<{ - showHidden: boolean; - depth: number; - colors: boolean; - indentLevel: number; - }>; - /** A symbol which can be used as a key for a custom method which will be called - * when `Deno.inspect()` is called, or when the object is logged to the console. - */ - export const customInspect: unique symbol; - /** - * `inspect()` converts input into string that has the same format - * as printed by `console.log(...)`; - */ - export function inspect(value: unknown, options?: ConsoleOptions): string; - - // @url js/build.d.ts - - export type OperatingSystem = "mac" | "win" | "linux"; - export type Arch = "x64" | "arm64"; - /** Build related information */ - interface BuildInfo { - /** The CPU architecture. */ - arch: Arch; - /** The operating system. */ - os: OperatingSystem; - } - export const build: BuildInfo; - - // @url js/version.d.ts - - interface Version { - deno: string; - v8: string; - typescript: string; - } - export const version: Version; - export {}; - - // @url js/deno.d.ts - - export const args: string[]; -} - -// @url js/globals.ts - -declare interface Window { - window: Window & typeof globalThis; - atob: typeof textEncoding.atob; - btoa: typeof textEncoding.btoa; - fetch: typeof fetchTypes.fetch; - clearTimeout: typeof timers.clearTimeout; - clearInterval: typeof timers.clearInterval; - console: consoleTypes.Console; - setTimeout: typeof timers.setTimeout; - setInterval: typeof timers.setInterval; - location: domTypes.Location; - onload: Function | undefined; - onunload: Function | undefined; - crypto: Crypto; - Blob: typeof blob.DenoBlob; - File: domTypes.DomFileConstructor; - CustomEvent: typeof customEvent.CustomEvent; - Event: typeof event.Event; - EventTarget: typeof eventTarget.EventTarget; - URL: typeof url.URL; - URLSearchParams: typeof urlSearchParams.URLSearchParams; - Headers: domTypes.HeadersConstructor; - FormData: domTypes.FormDataConstructor; - TextEncoder: typeof textEncoding.TextEncoder; - TextDecoder: typeof textEncoding.TextDecoder; - Request: domTypes.RequestConstructor; - Response: typeof fetchTypes.Response; - performance: performanceUtil.Performance; - onmessage: (e: { data: any }) => void; - workerMain: typeof workers.workerMain; - workerClose: typeof workers.workerClose; - postMessage: typeof workers.postMessage; - Worker: typeof workers.WorkerImpl; - addEventListener: ( - type: string, - callback: (event: domTypes.Event) => void | null, - options?: boolean | domTypes.AddEventListenerOptions | undefined - ) => void; - dispatchEvent: (event: domTypes.Event) => boolean; - removeEventListener: ( - type: string, - callback: (event: domTypes.Event) => void | null, - options?: boolean | domTypes.EventListenerOptions | undefined - ) => void; - queueMicrotask: (task: () => void) => void; - Deno: typeof Deno; -} - -declare const window: Window & typeof globalThis; -declare const atob: typeof textEncoding.atob; -declare const btoa: typeof textEncoding.btoa; -declare const fetch: typeof fetchTypes.fetch; -declare const clearTimeout: typeof timers.clearTimeout; -declare const clearInterval: typeof timers.clearInterval; -declare const console: consoleTypes.Console; -declare const setTimeout: typeof timers.setTimeout; -declare const setInterval: typeof timers.setInterval; -declare const location: domTypes.Location; -declare const onload: Function | undefined; -declare const onunload: Function | undefined; -declare const crypto: Crypto; -declare const Blob: typeof blob.DenoBlob; -declare const File: domTypes.DomFileConstructor; -declare const CustomEventInit: typeof customEvent.CustomEventInit; -declare const CustomEvent: typeof customEvent.CustomEvent; -declare const EventInit: typeof event.EventInit; -declare const Event: typeof event.Event; -declare const EventListener: typeof eventTarget.EventListener; -declare const EventTarget: typeof eventTarget.EventTarget; -declare const URL: typeof url.URL; -declare const URLSearchParams: typeof urlSearchParams.URLSearchParams; -declare const Headers: domTypes.HeadersConstructor; -declare const FormData: domTypes.FormDataConstructor; -declare const TextEncoder: typeof textEncoding.TextEncoder; -declare const TextDecoder: typeof textEncoding.TextDecoder; -declare const Request: domTypes.RequestConstructor; -declare const Response: typeof fetchTypes.Response; -declare const performance: performanceUtil.Performance; -declare let onmessage: (e: { data: any }) => void; -declare const workerMain: typeof workers.workerMain; -declare const workerClose: typeof workers.workerClose; -declare const postMessage: typeof workers.postMessage; -declare const Worker: typeof workers.WorkerImpl; -declare const addEventListener: ( - type: string, - callback: (event: domTypes.Event) => void | null, - options?: boolean | domTypes.AddEventListenerOptions | undefined -) => void; -declare const dispatchEvent: (event: domTypes.Event) => boolean; -declare const removeEventListener: ( - type: string, - callback: (event: domTypes.Event) => void | null, - options?: boolean | domTypes.EventListenerOptions | undefined -) => void; - -declare type Blob = domTypes.Blob; -declare type Body = domTypes.Body; -declare type File = domTypes.DomFile; -declare type CustomEventInit = domTypes.CustomEventInit; -declare type CustomEvent = domTypes.CustomEvent; -declare type EventInit = domTypes.EventInit; -declare type Event = domTypes.Event; -declare type EventListener = domTypes.EventListener; -declare type EventTarget = domTypes.EventTarget; -declare type URL = url.URL; -declare type URLSearchParams = domTypes.URLSearchParams; -declare type Headers = domTypes.Headers; -declare type FormData = domTypes.FormData; -declare type TextEncoder = textEncoding.TextEncoder; -declare type TextDecoder = textEncoding.TextDecoder; -declare type Request = domTypes.Request; -declare type Response = domTypes.Response; -declare type Worker = workers.Worker; - -declare interface ImportMeta { - url: string; - main: boolean; -} - -declare interface Crypto { - readonly subtle: null; - getRandomValues: < - T extends - | Int8Array - | Uint8Array - | Uint8ClampedArray - | Int16Array - | Uint16Array - | Int32Array - | Uint32Array - >( - typedArray: T - ) => T; -} - -declare namespace domTypes { - // @url js/dom_types.d.ts - - export type BufferSource = ArrayBufferView | ArrayBuffer; - export type HeadersInit = - | Headers - | Array<[string, string]> - | Record<string, string>; - export type URLSearchParamsInit = - | string - | string[][] - | Record<string, string>; - type BodyInit = - | Blob - | BufferSource - | FormData - | URLSearchParams - | ReadableStream - | string; - export type RequestInfo = Request | string; - type ReferrerPolicy = - | "" - | "no-referrer" - | "no-referrer-when-downgrade" - | "origin-only" - | "origin-when-cross-origin" - | "unsafe-url"; - export type BlobPart = BufferSource | Blob | string; - export type FormDataEntryValue = DomFile | string; - export interface DomIterable<K, V> { - keys(): IterableIterator<K>; - values(): IterableIterator<V>; - entries(): IterableIterator<[K, V]>; - [Symbol.iterator](): IterableIterator<[K, V]>; - forEach( - callback: (value: V, key: K, parent: this) => void, - thisArg?: any - ): void; - } - type EndingType = "transparent" | "native"; - export interface BlobPropertyBag { - type?: string; - ending?: EndingType; - } - interface AbortSignalEventMap { - abort: ProgressEvent; - } - export enum NodeType { - ELEMENT_NODE = 1, - TEXT_NODE = 3, - DOCUMENT_FRAGMENT_NODE = 11 - } - export const eventTargetHost: unique symbol; - export const eventTargetListeners: unique symbol; - export const eventTargetMode: unique symbol; - export const eventTargetNodeType: unique symbol; - export interface EventTarget { - [eventTargetHost]: EventTarget | null; - [eventTargetListeners]: { [type in string]: EventListener[] }; - [eventTargetMode]: string; - [eventTargetNodeType]: NodeType; - addEventListener( - type: string, - callback: (event: Event) => void | null, - options?: boolean | AddEventListenerOptions - ): void; - dispatchEvent(event: Event): boolean; - removeEventListener( - type: string, - callback?: (event: Event) => void | null, - options?: EventListenerOptions | boolean - ): void; - } - export interface ProgressEventInit extends EventInit { - lengthComputable?: boolean; - loaded?: number; - total?: number; - } - export interface URLSearchParams extends DomIterable<string, string> { - /** - * Appends a specified key/value pair as a new search parameter. - */ - append(name: string, value: string): void; - /** - * Deletes the given search parameter, and its associated value, - * from the list of all search parameters. - */ - delete(name: string): void; - /** - * Returns the first value associated to the given search parameter. - */ - get(name: string): string | null; - /** - * Returns all the values association with a given search parameter. - */ - getAll(name: string): string[]; - /** - * Returns a Boolean indicating if such a search parameter exists. - */ - has(name: string): boolean; - /** - * Sets the value associated to a given search parameter to the given value. - * If there were several values, delete the others. - */ - set(name: string, value: string): void; - /** - * Sort all key/value pairs contained in this object in place - * and return undefined. The sort order is according to Unicode - * code points of the keys. - */ - sort(): void; - /** - * Returns a query string suitable for use in a URL. - */ - toString(): string; - /** - * Iterates over each name-value pair in the query - * and invokes the given function. - */ - forEach( - callbackfn: (value: string, key: string, parent: this) => void, - thisArg?: any - ): void; - } - export interface EventListener { - handleEvent(event: Event): void; - readonly callback: (event: Event) => void | null; - readonly options: boolean | AddEventListenerOptions; - } - export interface EventInit { - bubbles?: boolean; - cancelable?: boolean; - composed?: boolean; - } - export interface CustomEventInit extends EventInit { - detail?: any; - } - export enum EventPhase { - NONE = 0, - CAPTURING_PHASE = 1, - AT_TARGET = 2, - BUBBLING_PHASE = 3 - } - export interface EventPath { - item: EventTarget; - itemInShadowTree: boolean; - relatedTarget: EventTarget | null; - rootOfClosedTree: boolean; - slotInClosedTree: boolean; - target: EventTarget | null; - touchTargetList: EventTarget[]; - } - export interface Event { - readonly type: string; - target: EventTarget | null; - currentTarget: EventTarget | null; - composedPath(): EventPath[]; - eventPhase: number; - stopPropagation(): void; - stopImmediatePropagation(): void; - readonly bubbles: boolean; - readonly cancelable: boolean; - preventDefault(): void; - readonly defaultPrevented: boolean; - readonly composed: boolean; - isTrusted: boolean; - readonly timeStamp: Date; - dispatched: boolean; - readonly initialized: boolean; - inPassiveListener: boolean; - cancelBubble: boolean; - cancelBubbleImmediately: boolean; - path: EventPath[]; - relatedTarget: EventTarget | null; - } - export interface CustomEvent extends Event { - readonly detail: any; - initCustomEvent( - type: string, - bubbles?: boolean, - cancelable?: boolean, - detail?: any | null - ): void; - } - export interface DomFile extends Blob { - readonly lastModified: number; - readonly name: string; - } - export interface DomFileConstructor { - new ( - bits: BlobPart[], - filename: string, - options?: FilePropertyBag - ): DomFile; - prototype: DomFile; - } - export interface FilePropertyBag extends BlobPropertyBag { - lastModified?: number; - } - interface ProgressEvent extends Event { - readonly lengthComputable: boolean; - readonly loaded: number; - readonly total: number; - } - export interface EventListenerOptions { - capture: boolean; - } - export interface AddEventListenerOptions extends EventListenerOptions { - once: boolean; - passive: boolean; - } - interface AbortSignal extends EventTarget { - readonly aborted: boolean; - onabort: ((this: AbortSignal, ev: ProgressEvent) => any) | null; - addEventListener<K extends keyof AbortSignalEventMap>( - type: K, - listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, - options?: boolean | AddEventListenerOptions - ): void; - addEventListener( - type: string, - listener: EventListener, - options?: boolean | AddEventListenerOptions - ): void; - removeEventListener<K extends keyof AbortSignalEventMap>( - type: K, - listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, - options?: boolean | EventListenerOptions - ): void; - removeEventListener( - type: string, - listener: EventListener, - options?: boolean | EventListenerOptions - ): void; - } - export interface ReadableStream { - readonly locked: boolean; - cancel(): Promise<void>; - getReader(): ReadableStreamReader; - tee(): [ReadableStream, ReadableStream]; - } - export interface ReadableStreamReader { - cancel(): Promise<void>; - read(): Promise<any>; - releaseLock(): void; - } - export interface FormData extends DomIterable<string, FormDataEntryValue> { - append(name: string, value: string | Blob, fileName?: string): void; - delete(name: string): void; - get(name: string): FormDataEntryValue | null; - getAll(name: string): FormDataEntryValue[]; - has(name: string): boolean; - set(name: string, value: string | Blob, fileName?: string): void; - } - export interface FormDataConstructor { - new (): FormData; - prototype: FormData; - } - /** A blob object represents a file-like object of immutable, raw data. */ - export interface Blob { - /** The size, in bytes, of the data contained in the `Blob` object. */ - readonly size: number; - /** A string indicating the media type of the data contained in the `Blob`. - * If the type is unknown, this string is empty. - */ - readonly type: string; - /** Returns a new `Blob` object containing the data in the specified range of - * bytes of the source `Blob`. - */ - slice(start?: number, end?: number, contentType?: string): Blob; - } - export interface Body { - /** A simple getter used to expose a `ReadableStream` of the body contents. */ - readonly body: ReadableStream | null; - /** Stores a `Boolean` that declares whether the body has been used in a - * response yet. - */ - readonly bodyUsed: boolean; - /** Takes a `Response` stream and reads it to completion. It returns a promise - * that resolves with an `ArrayBuffer`. - */ - arrayBuffer(): Promise<ArrayBuffer>; - /** Takes a `Response` stream and reads it to completion. It returns a promise - * that resolves with a `Blob`. - */ - blob(): Promise<Blob>; - /** Takes a `Response` stream and reads it to completion. It returns a promise - * that resolves with a `FormData` object. - */ - formData(): Promise<FormData>; - /** Takes a `Response` stream and reads it to completion. It returns a promise - * that resolves with the result of parsing the body text as JSON. - */ - json(): Promise<any>; - /** Takes a `Response` stream and reads it to completion. It returns a promise - * that resolves with a `USVString` (text). - */ - text(): Promise<string>; - } - export interface Headers extends DomIterable<string, string> { - /** Appends a new value onto an existing header inside a `Headers` object, or - * adds the header if it does not already exist. - */ - append(name: string, value: string): void; - /** Deletes a header from a `Headers` object. */ - delete(name: string): void; - /** Returns an iterator allowing to go through all key/value pairs - * contained in this Headers object. The both the key and value of each pairs - * are ByteString objects. - */ - entries(): IterableIterator<[string, string]>; - /** Returns a `ByteString` sequence of all the values of a header within a - * `Headers` object with a given name. - */ - get(name: string): string | null; - /** Returns a boolean stating whether a `Headers` object contains a certain - * header. - */ - has(name: string): boolean; - /** Returns an iterator allowing to go through all keys contained in - * this Headers object. The keys are ByteString objects. - */ - keys(): IterableIterator<string>; - /** Sets a new value for an existing header inside a Headers object, or adds - * the header if it does not already exist. - */ - set(name: string, value: string): void; - /** Returns an iterator allowing to go through all values contained in - * this Headers object. The values are ByteString objects. - */ - values(): IterableIterator<string>; - forEach( - callbackfn: (value: string, key: string, parent: this) => void, - thisArg?: any - ): void; - /** The Symbol.iterator well-known symbol specifies the default - * iterator for this Headers object - */ - [Symbol.iterator](): IterableIterator<[string, string]>; - } - export interface HeadersConstructor { - new (init?: HeadersInit): Headers; - prototype: Headers; - } - type RequestCache = - | "default" - | "no-store" - | "reload" - | "no-cache" - | "force-cache" - | "only-if-cached"; - type RequestCredentials = "omit" | "same-origin" | "include"; - type RequestDestination = - | "" - | "audio" - | "audioworklet" - | "document" - | "embed" - | "font" - | "image" - | "manifest" - | "object" - | "paintworklet" - | "report" - | "script" - | "sharedworker" - | "style" - | "track" - | "video" - | "worker" - | "xslt"; - type RequestMode = "navigate" | "same-origin" | "no-cors" | "cors"; - type RequestRedirect = "follow" | "error" | "manual"; - type ResponseType = - | "basic" - | "cors" - | "default" - | "error" - | "opaque" - | "opaqueredirect"; - export interface RequestInit { - body?: BodyInit | null; - cache?: RequestCache; - credentials?: RequestCredentials; - headers?: HeadersInit; - integrity?: string; - keepalive?: boolean; - method?: string; - mode?: RequestMode; - redirect?: RequestRedirect; - referrer?: string; - referrerPolicy?: ReferrerPolicy; - signal?: AbortSignal | null; - window?: any; - } - export interface ResponseInit { - headers?: HeadersInit; - status?: number; - statusText?: string; - } - export interface RequestConstructor { - new (input: RequestInfo, init?: RequestInit): Request; - prototype: Request; - } - export interface Request extends Body { - /** Returns the cache mode associated with request, which is a string - * indicating how the the request will interact with the browser's cache when - * fetching. - */ - readonly cache?: RequestCache; - /** Returns the credentials mode associated with request, which is a string - * indicating whether credentials will be sent with the request always, never, - * or only when sent to a same-origin URL. - */ - readonly credentials?: RequestCredentials; - /** Returns the kind of resource requested by request, (e.g., `document` or - * `script`). - */ - readonly destination?: RequestDestination; - /** Returns a Headers object consisting of the headers associated with - * request. - * - * Note that headers added in the network layer by the user agent - * will not be accounted for in this object, (e.g., the `Host` header). - */ - readonly headers: Headers; - /** Returns request's subresource integrity metadata, which is a cryptographic - * hash of the resource being fetched. Its value consists of multiple hashes - * separated by whitespace. [SRI] - */ - readonly integrity?: string; - /** Returns a boolean indicating whether or not request is for a history - * navigation (a.k.a. back-forward navigation). - */ - readonly isHistoryNavigation?: boolean; - /** Returns a boolean indicating whether or not request is for a reload - * navigation. - */ - readonly isReloadNavigation?: boolean; - /** Returns a boolean indicating whether or not request can outlive the global - * in which it was created. - */ - readonly keepalive?: boolean; - /** Returns request's HTTP method, which is `GET` by default. */ - readonly method: string; - /** Returns the mode associated with request, which is a string indicating - * whether the request will use CORS, or will be restricted to same-origin - * URLs. - */ - readonly mode?: RequestMode; - /** Returns the redirect mode associated with request, which is a string - * indicating how redirects for the request will be handled during fetching. - * - * A request will follow redirects by default. - */ - readonly redirect?: RequestRedirect; - /** Returns the referrer of request. Its value can be a same-origin URL if - * explicitly set in init, the empty string to indicate no referrer, and - * `about:client` when defaulting to the global's default. - * - * This is used during fetching to determine the value of the `Referer` - * header of the request being made. - */ - readonly referrer?: string; - /** Returns the referrer policy associated with request. This is used during - * fetching to compute the value of the request's referrer. - */ - readonly referrerPolicy?: ReferrerPolicy; - /** Returns the signal associated with request, which is an AbortSignal object - * indicating whether or not request has been aborted, and its abort event - * handler. - */ - readonly signal?: AbortSignal; - /** Returns the URL of request as a string. */ - readonly url: string; - clone(): Request; - } - export interface Response extends Body { - /** Contains the `Headers` object associated with the response. */ - readonly headers: Headers; - /** Contains a boolean stating whether the response was successful (status in - * the range 200-299) or not. - */ - readonly ok: boolean; - /** Indicates whether or not the response is the result of a redirect; that - * is, its URL list has more than one entry. - */ - readonly redirected: boolean; - /** Contains the status code of the response (e.g., `200` for a success). */ - readonly status: number; - /** Contains the status message corresponding to the status code (e.g., `OK` - * for `200`). - */ - readonly statusText: string; - readonly trailer: Promise<Headers>; - /** Contains the type of the response (e.g., `basic`, `cors`). */ - readonly type: ResponseType; - /** Contains the URL of the response. */ - readonly url: string; - /** Creates a clone of a `Response` object. */ - clone(): Response; - } - export interface Location { - /** - * Returns a DOMStringList object listing the origins of the ancestor browsing - * contexts, from the parent browsing context to the top-level browsing - * context. - */ - readonly ancestorOrigins: string[]; - /** - * Returns the Location object's URL's fragment (includes leading "#" if - * non-empty). - * Can be set, to navigate to the same URL with a changed fragment (ignores - * leading "#"). - */ - hash: string; - /** - * Returns the Location object's URL's host and port (if different from the - * default port for the scheme). Can be set, to navigate to the same URL with - * a changed host and port. - */ - host: string; - /** - * Returns the Location object's URL's host. Can be set, to navigate to the - * same URL with a changed host. - */ - hostname: string; - /** - * Returns the Location object's URL. Can be set, to navigate to the given - * URL. - */ - href: string; - /** Returns the Location object's URL's origin. */ - readonly origin: string; - /** - * Returns the Location object's URL's path. - * Can be set, to navigate to the same URL with a changed path. - */ - pathname: string; - /** - * Returns the Location object's URL's port. - * Can be set, to navigate to the same URL with a changed port. - */ - port: string; - /** - * Returns the Location object's URL's scheme. - * Can be set, to navigate to the same URL with a changed scheme. - */ - protocol: string; - /** - * Returns the Location object's URL's query (includes leading "?" if - * non-empty). Can be set, to navigate to the same URL with a changed query - * (ignores leading "?"). - */ - search: string; - /** - * Navigates to the given URL. - */ - assign(url: string): void; - /** - * Reloads the current page. - */ - reload(): void; - /** @deprecated */ - reload(forcedReload: boolean): void; - /** - * Removes the current page from the session history and navigates to the - * given URL. - */ - replace(url: string): void; - } -} - -declare namespace blob { - // @url js/blob.d.ts - - export const bytesSymbol: unique symbol; - export const blobBytesWeakMap: WeakMap<domTypes.Blob, Uint8Array>; - export class DenoBlob implements domTypes.Blob { - private readonly [bytesSymbol]; - readonly size: number; - readonly type: string; - /** A blob object represents a file-like object of immutable, raw data. */ - constructor( - blobParts?: domTypes.BlobPart[], - options?: domTypes.BlobPropertyBag - ); - slice(start?: number, end?: number, contentType?: string): DenoBlob; - } -} - -declare namespace consoleTypes { - // @url js/console.d.ts - - type ConsoleOptions = Partial<{ - showHidden: boolean; - depth: number; - colors: boolean; - indentLevel: number; - }>; - export class CSI { - static kClear: string; - static kClearScreenDown: string; - } - const isConsoleInstance: unique symbol; - export class Console { - private printFunc; - indentLevel: number; - [isConsoleInstance]: boolean; - /** Writes the arguments to stdout */ - log: (...args: unknown[]) => void; - /** Writes the arguments to stdout */ - debug: (...args: unknown[]) => void; - /** Writes the arguments to stdout */ - info: (...args: unknown[]) => void; - /** Writes the properties of the supplied `obj` to stdout */ - dir: ( - obj: unknown, - options?: Partial<{ - showHidden: boolean; - depth: number; - colors: boolean; - indentLevel: number; - }> - ) => void; - - /** From MDN: - * Displays an interactive tree of the descendant elements of - * the specified XML/HTML element. If it is not possible to display - * as an element the JavaScript Object view is shown instead. - * The output is presented as a hierarchical listing of expandable - * nodes that let you see the contents of child nodes. - * - * Since we write to stdout, we can't display anything interactive - * we just fall back to `console.dir`. - */ - dirxml: ( - obj: unknown, - options?: Partial<{ - showHidden: boolean; - depth: number; - colors: boolean; - indentLevel: number; - }> - ) => void; - - /** Writes the arguments to stdout */ - warn: (...args: unknown[]) => void; - /** Writes the arguments to stdout */ - error: (...args: unknown[]) => void; - /** Writes an error message to stdout if the assertion is `false`. If the - * assertion is `true`, nothing happens. - * - * ref: https://console.spec.whatwg.org/#assert - */ - assert: (condition?: boolean, ...args: unknown[]) => void; - count: (label?: string) => void; - countReset: (label?: string) => void; - table: (data: unknown, properties?: string[] | undefined) => void; - time: (label?: string) => void; - timeLog: (label?: string, ...args: unknown[]) => void; - timeEnd: (label?: string) => void; - group: (...label: unknown[]) => void; - groupCollapsed: (...label: unknown[]) => void; - groupEnd: () => void; - clear: () => void; - trace: (...args: unknown[]) => void; - static [Symbol.hasInstance](instance: Console): boolean; - } - /** A symbol which can be used as a key for a custom method which will be called - * when `Deno.inspect()` is called, or when the object is logged to the console. - */ - export const customInspect: unique symbol; - /** - * `inspect()` converts input into string that has the same format - * as printed by `console.log(...)`; - */ - export function inspect(value: unknown, options?: ConsoleOptions): string; -} - -declare namespace event { - // @url js/event.d.ts - - export const eventAttributes: WeakMap<object, any>; - export class EventInit implements domTypes.EventInit { - bubbles: boolean; - cancelable: boolean; - composed: boolean; - constructor({ - bubbles, - cancelable, - composed - }?: { - bubbles?: boolean | undefined; - cancelable?: boolean | undefined; - composed?: boolean | undefined; - }); - } - export class Event implements domTypes.Event { - isTrusted: boolean; - private _canceledFlag; - private _dispatchedFlag; - private _initializedFlag; - private _inPassiveListenerFlag; - private _stopImmediatePropagationFlag; - private _stopPropagationFlag; - private _path; - constructor(type: string, eventInitDict?: domTypes.EventInit); - readonly bubbles: boolean; - cancelBubble: boolean; - cancelBubbleImmediately: boolean; - readonly cancelable: boolean; - readonly composed: boolean; - currentTarget: domTypes.EventTarget; - readonly defaultPrevented: boolean; - dispatched: boolean; - eventPhase: number; - readonly initialized: boolean; - inPassiveListener: boolean; - path: domTypes.EventPath[]; - relatedTarget: domTypes.EventTarget; - target: domTypes.EventTarget; - readonly timeStamp: Date; - readonly type: string; - /** Returns the event’s path (objects on which listeners will be - * invoked). This does not include nodes in shadow trees if the - * shadow root was created with its ShadowRoot.mode closed. - * - * event.composedPath(); - */ - composedPath(): domTypes.EventPath[]; - /** Cancels the event (if it is cancelable). - * See https://dom.spec.whatwg.org/#set-the-canceled-flag - * - * event.preventDefault(); - */ - preventDefault(): void; - /** Stops the propagation of events further along in the DOM. - * - * event.stopPropagation(); - */ - stopPropagation(): void; - /** For this particular event, no other listener will be called. - * Neither those attached on the same element, nor those attached - * on elements which will be traversed later (in capture phase, - * for instance). - * - * event.stopImmediatePropagation(); - */ - stopImmediatePropagation(): void; - } -} - -declare namespace customEvent { - // @url js/custom_event.d.ts - - export const customEventAttributes: WeakMap<object, any>; - export class CustomEventInit extends event.EventInit - implements domTypes.CustomEventInit { - detail: any; - constructor({ - bubbles, - cancelable, - composed, - detail - }: domTypes.CustomEventInit); - } - export class CustomEvent extends event.Event implements domTypes.CustomEvent { - constructor(type: string, customEventInitDict?: domTypes.CustomEventInit); - readonly detail: any; - initCustomEvent( - type: string, - bubbles?: boolean, - cancelable?: boolean, - detail?: any - ): void; - readonly [Symbol.toStringTag]: string; - } -} - -declare namespace eventTarget { - // @url js/event_target.d.ts - - export class EventListenerOptions implements domTypes.EventListenerOptions { - _capture: boolean; - constructor({ capture }?: { capture?: boolean | undefined }); - readonly capture: boolean; - } - export class AddEventListenerOptions extends EventListenerOptions - implements domTypes.AddEventListenerOptions { - _passive: boolean; - _once: boolean; - constructor({ - capture, - passive, - once - }?: { - capture?: boolean | undefined; - passive?: boolean | undefined; - once?: boolean | undefined; - }); - readonly passive: boolean; - readonly once: boolean; - } - export class EventListener implements domTypes.EventListener { - allEvents: domTypes.Event[]; - atEvents: domTypes.Event[]; - bubbledEvents: domTypes.Event[]; - capturedEvents: domTypes.Event[]; - private _callback; - private _options; - constructor( - callback: (event: domTypes.Event) => void | null, - options: boolean | domTypes.AddEventListenerOptions - ); - handleEvent(event: domTypes.Event): void; - readonly callback: (event: domTypes.Event) => void | null; - readonly options: domTypes.AddEventListenerOptions | boolean; - } - export const eventTargetAssignedSlot: unique symbol; - export const eventTargetHasActivationBehavior: unique symbol; - export class EventTarget implements domTypes.EventTarget { - [domTypes.eventTargetHost]: domTypes.EventTarget | null; - [domTypes.eventTargetListeners]: { - [type in string]: domTypes.EventListener[] - }; - [domTypes.eventTargetMode]: string; - [domTypes.eventTargetNodeType]: domTypes.NodeType; - private [eventTargetAssignedSlot]; - private [eventTargetHasActivationBehavior]; - addEventListener( - type: string, - callback: (event: domTypes.Event) => void | null, - options?: domTypes.AddEventListenerOptions | boolean - ): void; - removeEventListener( - type: string, - callback: (event: domTypes.Event) => void | null, - options?: domTypes.EventListenerOptions | boolean - ): void; - dispatchEvent(event: domTypes.Event): boolean; - readonly [Symbol.toStringTag]: string; - } -} - -declare namespace io { - // @url js/io.d.ts - - export const EOF: null; - export type EOF = null; - export enum SeekMode { - SEEK_START = 0, - SEEK_CURRENT = 1, - SEEK_END = 2 - } - export interface Reader { - /** Reads up to p.byteLength bytes into `p`. It resolves to the number - * of bytes read (`0` < `n` <= `p.byteLength`) and rejects if any error encountered. - * Even if `read()` returns `n` < `p.byteLength`, it may use all of `p` as - * scratch space during the call. If some data is available but not - * `p.byteLength` bytes, `read()` conventionally returns what is available - * instead of waiting for more. - * - * When `read()` encounters end-of-file condition, it returns EOF symbol. - * - * When `read()` encounters an error, it rejects with an error. - * - * Callers should always process the `n` > `0` bytes returned before - * considering the EOF. Doing so correctly handles I/O errors that happen - * after reading some bytes and also both of the allowed EOF behaviors. - * - * Implementations must not retain `p`. - */ - read(p: Uint8Array): Promise<number | EOF>; - } - export interface SyncReader { - readSync(p: Uint8Array): number | EOF; - } - export interface Writer { - /** Writes `p.byteLength` bytes from `p` to the underlying data - * stream. It resolves to the number of bytes written from `p` (`0` <= `n` <= - * `p.byteLength`) and any error encountered that caused the write to stop - * early. `write()` must return a non-null error if it returns `n` < - * `p.byteLength`. write() must not modify the slice data, even temporarily. - * - * Implementations must not retain `p`. - */ - write(p: Uint8Array): Promise<number>; - } - export interface SyncWriter { - writeSync(p: Uint8Array): number; - } - export interface Closer { - close(): void; - } - export interface Seeker { - /** Seek sets the offset for the next `read()` or `write()` to offset, - * interpreted according to `whence`: `SeekStart` means relative to the start - * of the file, `SeekCurrent` means relative to the current offset, and - * `SeekEnd` means relative to the end. Seek returns the new offset relative - * to the start of the file and an error, if any. - * - * Seeking to an offset before the start of the file is an error. Seeking to - * any positive offset is legal, but the behavior of subsequent I/O operations - * on the underlying object is implementation-dependent. - */ - seek(offset: number, whence: SeekMode): Promise<void>; - } - export interface SyncSeeker { - seekSync(offset: number, whence: SeekMode): void; - } - export interface ReadCloser extends Reader, Closer {} - export interface WriteCloser extends Writer, Closer {} - export interface ReadSeeker extends Reader, Seeker {} - export interface WriteSeeker extends Writer, Seeker {} - export interface ReadWriteCloser extends Reader, Writer, Closer {} - export interface ReadWriteSeeker extends Reader, Writer, Seeker {} - /** Copies from `src` to `dst` until either `EOF` is reached on `src` - * or an error occurs. It returns the number of bytes copied and the first - * error encountered while copying, if any. - * - * Because `copy()` is defined to read from `src` until `EOF`, it does not - * treat an `EOF` from `read()` as an error to be reported. - */ - export function copy(dst: Writer, src: Reader): Promise<number>; - /** Turns `r` into async iterator. - * - * for await (const chunk of toAsyncIterator(reader)) { - * console.log(chunk) - * } - */ - export function toAsyncIterator(r: Reader): AsyncIterableIterator<Uint8Array>; -} - -declare namespace fetchTypes { - // @url js/fetch.d.ts - - class Body implements domTypes.Body, domTypes.ReadableStream, io.ReadCloser { - private rid; - readonly contentType: string; - bodyUsed: boolean; - private _bodyPromise; - private _data; - readonly locked: boolean; - readonly body: null | Body; - constructor(rid: number, contentType: string); - private _bodyBuffer; - arrayBuffer(): Promise<ArrayBuffer>; - blob(): Promise<domTypes.Blob>; - formData(): Promise<domTypes.FormData>; - json(): Promise<any>; - text(): Promise<string>; - read(p: Uint8Array): Promise<number | io.EOF>; - close(): void; - cancel(): Promise<void>; - getReader(): domTypes.ReadableStreamReader; - tee(): [domTypes.ReadableStream, domTypes.ReadableStream]; - [Symbol.asyncIterator](): AsyncIterableIterator<Uint8Array>; - } - export class Response implements domTypes.Response { - readonly url: string; - readonly status: number; - statusText: string; - readonly type = "basic"; - readonly redirected: boolean; - headers: domTypes.Headers; - readonly trailer: Promise<domTypes.Headers>; - bodyUsed: boolean; - readonly body: Body; - constructor( - url: string, - status: number, - headersList: Array<[string, string]>, - rid: number, - redirected_: boolean, - body_?: null | Body - ); - arrayBuffer(): Promise<ArrayBuffer>; - blob(): Promise<domTypes.Blob>; - formData(): Promise<domTypes.FormData>; - json(): Promise<any>; - text(): Promise<string>; - readonly ok: boolean; - clone(): domTypes.Response; - } - /** Fetch a resource from the network. */ - export function fetch( - input: domTypes.Request | string, - init?: domTypes.RequestInit - ): Promise<Response>; -} - -declare namespace textEncoding { - // @url js/text_encoding.d.ts - - export function atob(s: string): string; - /** Creates a base-64 ASCII string from the input string. */ - export function btoa(s: string): string; - export interface TextDecodeOptions { - stream?: false; - } - export interface TextDecoderOptions { - fatal?: boolean; - ignoreBOM?: boolean; - } - export class TextDecoder { - private _encoding; - /** Returns encoding's name, lowercased. */ - readonly encoding: string; - /** Returns `true` if error mode is "fatal", and `false` otherwise. */ - readonly fatal: boolean; - /** Returns `true` if ignore BOM flag is set, and `false` otherwise. */ - readonly ignoreBOM = false; - constructor(label?: string, options?: TextDecoderOptions); - /** Returns the result of running encoding's decoder. */ - decode(input?: domTypes.BufferSource, options?: TextDecodeOptions): string; - readonly [Symbol.toStringTag]: string; - } - interface TextEncoderEncodeIntoResult { - read: number; - written: number; - } - export class TextEncoder { - /** Returns "utf-8". */ - readonly encoding = "utf-8"; - /** Returns the result of running UTF-8's encoder. */ - encode(input?: string): Uint8Array; - encodeInto(input: string, dest: Uint8Array): TextEncoderEncodeIntoResult; - readonly [Symbol.toStringTag]: string; - } -} - -declare namespace timers { - // @url js/timers.d.ts - - export type Args = unknown[]; - /** Sets a timer which executes a function once after the timer expires. */ - export function setTimeout( - cb: (...args: Args) => void, - delay?: number, - ...args: Args - ): number; - /** Repeatedly calls a function , with a fixed time delay between each call. */ - export function setInterval( - cb: (...args: Args) => void, - delay?: number, - ...args: Args - ): number; - export function clearTimeout(id?: number): void; - export function clearInterval(id?: number): void; -} - -declare namespace urlSearchParams { - // @url js/url_search_params.d.ts - - export class URLSearchParams { - private params; - private url; - constructor(init?: string | string[][] | Record<string, string>); - private updateSteps; - /** Appends a specified key/value pair as a new search parameter. - * - * searchParams.append('name', 'first'); - * searchParams.append('name', 'second'); - */ - append(name: string, value: string): void; - /** Deletes the given search parameter and its associated value, - * from the list of all search parameters. - * - * searchParams.delete('name'); - */ - delete(name: string): void; - /** Returns all the values associated with a given search parameter - * as an array. - * - * searchParams.getAll('name'); - */ - getAll(name: string): string[]; - /** Returns the first value associated to the given search parameter. - * - * searchParams.get('name'); - */ - get(name: string): string | null; - /** Returns a Boolean that indicates whether a parameter with the - * specified name exists. - * - * searchParams.has('name'); - */ - has(name: string): boolean; - /** Sets the value associated with a given search parameter to the - * given value. If there were several matching values, this method - * deletes the others. If the search parameter doesn't exist, this - * method creates it. - * - * searchParams.set('name', 'value'); - */ - set(name: string, value: string): void; - /** Sort all key/value pairs contained in this object in place and - * return undefined. The sort order is according to Unicode code - * points of the keys. - * - * searchParams.sort(); - */ - sort(): void; - /** Calls a function for each element contained in this object in - * place and return undefined. Optionally accepts an object to use - * as this when executing callback as second argument. - * - * searchParams.forEach((value, key, parent) => { - * console.log(value, key, parent); - * }); - * - */ - forEach( - callbackfn: (value: string, key: string, parent: this) => void, - thisArg?: any - ): void; - /** Returns an iterator allowing to go through all keys contained - * in this object. - * - * for (const key of searchParams.keys()) { - * console.log(key); - * } - */ - keys(): IterableIterator<string>; - /** Returns an iterator allowing to go through all values contained - * in this object. - * - * for (const value of searchParams.values()) { - * console.log(value); - * } - */ - values(): IterableIterator<string>; - /** Returns an iterator allowing to go through all key/value - * pairs contained in this object. - * - * for (const [key, value] of searchParams.entries()) { - * console.log(key, value); - * } - */ - entries(): IterableIterator<[string, string]>; - /** Returns an iterator allowing to go through all key/value - * pairs contained in this object. - * - * for (const [key, value] of searchParams[Symbol.iterator]()) { - * console.log(key, value); - * } - */ - [Symbol.iterator](): IterableIterator<[string, string]>; - /** Returns a query string suitable for use in a URL. - * - * searchParams.toString(); - */ - toString(): string; - private _handleStringInitialization; - private _handleArrayInitialization; - } -} - -declare namespace url { - // @url js/url.d.ts - - export const blobURLMap: Map<string, domTypes.Blob>; - export class URL { - private _parts; - private _searchParams; - private _updateSearchParams; - hash: string; - host: string; - hostname: string; - href: string; - readonly origin: string; - password: string; - pathname: string; - port: string; - protocol: string; - search: string; - username: string; - readonly searchParams: urlSearchParams.URLSearchParams; - constructor(url: string, base?: string | URL); - toString(): string; - toJSON(): string; - static createObjectURL(b: domTypes.Blob): string; - static revokeObjectURL(url: string): void; - } -} - -declare namespace workers { - // @url js/workers.d.ts - - export function encodeMessage(data: any): Uint8Array; - export function decodeMessage(dataIntArray: Uint8Array): any; - export let onmessage: (e: { data: any }) => void; - export function postMessage(data: any): void; - export function getMessage(): Promise<any>; - export let isClosing: boolean; - export function workerClose(): void; - export function workerMain(): Promise<void>; - export interface Worker { - onerror?: () => void; - onmessage?: (e: { data: any }) => void; - onmessageerror?: () => void; - postMessage(data: any): void; - closed: Promise<void>; - } - export interface WorkerOptions {} - /** Extended Deno Worker initialization options. - * `noDenoNamespace` hides global `window.Deno` namespace for - * spawned worker and nested workers spawned by it (default: false). - */ - export interface DenoWorkerOptions extends WorkerOptions { - noDenoNamespace?: boolean; - } - export class WorkerImpl implements Worker { - private readonly rid; - private isClosing; - private readonly isClosedPromise; - onerror?: () => void; - onmessage?: (data: any) => void; - onmessageerror?: () => void; - constructor(specifier: string, options?: DenoWorkerOptions); - readonly closed: Promise<void>; - postMessage(data: any): void; - private run; - } -} - -declare namespace performanceUtil { - // @url js/performance.d.ts - - export class Performance { - /** Returns a current time from Deno's start in milliseconds. - * - * Use the flag --allow-hrtime return a precise value. - * - * const t = performance.now(); - * console.log(`${t} ms since start!`); - */ - now(): number; - } -} - -// @url js/lib.web_assembly.d.ts - -// This follows the WebIDL at: https://webassembly.github.io/spec/js-api/ -// And follow on WebIDL at: https://webassembly.github.io/spec/web-api/ - -/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any */ - -declare namespace WebAssembly { - interface WebAssemblyInstantiatedSource { - module: Module; - instance: Instance; - } - - /** Compiles a `WebAssembly.Module` from WebAssembly binary code. This - * function is useful if it is necessary to a compile a module before it can - * be instantiated (otherwise, the `WebAssembly.instantiate()` function - * should be used). */ - function compile(bufferSource: domTypes.BufferSource): Promise<Module>; - - /** Compiles a `WebAssembly.Module` directly from a streamed underlying - * source. This function is useful if it is necessary to a compile a module - * before it can be instantiated (otherwise, the - * `WebAssembly.instantiateStreaming()` function should be used). */ - function compileStreaming( - source: Promise<domTypes.Response> - ): Promise<Module>; - - /** Takes the WebAssembly binary code, in the form of a typed array or - * `ArrayBuffer`, and performs both compilation and instantiation in one step. - * The returned `Promise` resolves to both a compiled `WebAssembly.Module` and - * its first `WebAssembly.Instance`. */ - function instantiate( - bufferSource: domTypes.BufferSource, - importObject?: object - ): Promise<WebAssemblyInstantiatedSource>; - - /** Takes an already-compiled `WebAssembly.Module` and returns a `Promise` - * that resolves to an `Instance` of that `Module`. This overload is useful if - * the `Module` has already been compiled. */ - function instantiate( - module: Module, - importObject?: object - ): Promise<Instance>; - - /** Compiles and instantiates a WebAssembly module directly from a streamed - * underlying source. This is the most efficient, optimized way to load wasm - * code. */ - function instantiateStreaming( - source: Promise<domTypes.Response>, - importObject?: object - ): Promise<WebAssemblyInstantiatedSource>; - - /** Validates a given typed array of WebAssembly binary code, returning - * whether the bytes form a valid wasm module (`true`) or not (`false`). */ - function validate(bufferSource: domTypes.BufferSource): boolean; - - type ImportExportKind = "function" | "table" | "memory" | "global"; - - interface ModuleExportDescriptor { - name: string; - kind: ImportExportKind; - } - interface ModuleImportDescriptor { - module: string; - name: string; - kind: ImportExportKind; - } - - class Module { - constructor(bufferSource: domTypes.BufferSource); - - /** Given a `Module` and string, returns a copy of the contents of all - * custom sections in the module with the given string name. */ - static customSections( - moduleObject: Module, - sectionName: string - ): ArrayBuffer; - - /** Given a `Module`, returns an array containing descriptions of all the - * declared exports. */ - static exports(moduleObject: Module): ModuleExportDescriptor[]; - - /** Given a `Module`, returns an array containing descriptions of all the - * declared imports. */ - static imports(moduleObject: Module): ModuleImportDescriptor[]; - } - - class Instance<T extends object = { [key: string]: any }> { - constructor(module: Module, importObject?: object); - - /** An object containing as its members all the functions exported from the - * WebAssembly module instance, to allow them to be accessed and used by - * JavaScript. */ - readonly exports: T; - } - - interface MemoryDescriptor { - initial: number; - maximum?: number; - } - - class Memory { - constructor(descriptor: MemoryDescriptor); - - /** An accessor property that returns the buffer contained in the memory. */ - readonly buffer: ArrayBuffer; - - /** Increases the size of the memory instance by a specified number of - * WebAssembly pages (each one is 64KB in size). */ - grow(delta: number): number; - } - - type TableKind = "anyfunc"; - - interface TableDescriptor { - element: TableKind; - initial: number; - maximum?: number; - } - - class Table { - constructor(descriptor: TableDescriptor); - - /** Returns the length of the table, i.e. the number of elements. */ - readonly length: number; - - /** Accessor function — gets the element stored at a given index. */ - get(index: number): (...args: any[]) => any; - - /** Increases the size of the Table instance by a specified number of - * elements. */ - grow(delta: number): number; - - /** Sets an element stored at a given index to a given value. */ - set(index: number, value: (...args: any[]) => any): void; - } - - interface GlobalDescriptor { - value: string; - mutable?: boolean; - } - - /** Represents a global variable instance, accessible from both JavaScript and - * importable/exportable across one or more `WebAssembly.Module` instances. - * This allows dynamic linking of multiple modules. */ - class Global { - constructor(descriptor: GlobalDescriptor, value?: any); - - /** Old-style method that returns the value contained inside the global - * variable. */ - valueOf(): any; - - /** The value contained inside the global variable — this can be used to - * directly set and get the global's value. */ - value: any; - } - - /** Indicates an error during WebAssembly decoding or validation */ - class CompileError extends Error { - constructor(message: string, fileName?: string, lineNumber?: string); - } - - /** Indicates an error during module instantiation (besides traps from the - * start function). */ - class LinkError extends Error { - constructor(message: string, fileName?: string, lineNumber?: string); - } - - /** Is thrown whenever WebAssembly specifies a trap. */ - class RuntimeError extends Error { - constructor(message: string, fileName?: string, lineNumber?: string); - } -} - -/* eslint-enable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any */ diff --git a/js/lib.rs b/js/lib.rs deleted file mode 100644 index 89688b9a5..000000000 --- a/js/lib.rs +++ /dev/null @@ -1,55 +0,0 @@ -pub const TS_VERSION: &str = env!("TS_VERSION"); - -pub static CLI_SNAPSHOT: &[u8] = - include_bytes!(concat!(env!("OUT_DIR"), "/CLI_SNAPSHOT.bin")); -pub static CLI_SNAPSHOT_MAP: &[u8] = - include_bytes!(concat!(env!("OUT_DIR"), "/CLI_SNAPSHOT.js.map")); -pub static CLI_SNAPSHOT_DTS: &[u8] = - include_bytes!(concat!(env!("OUT_DIR"), "/CLI_SNAPSHOT.d.ts")); - -pub static COMPILER_SNAPSHOT: &[u8] = - include_bytes!(concat!(env!("OUT_DIR"), "/COMPILER_SNAPSHOT.bin")); -pub static COMPILER_SNAPSHOT_MAP: &[u8] = - include_bytes!(concat!(env!("OUT_DIR"), "/COMPILER_SNAPSHOT.js.map")); -pub static COMPILER_SNAPSHOT_DTS: &[u8] = - include_bytes!(concat!(env!("OUT_DIR"), "/COMPILER_SNAPSHOT.d.ts")); - -static DENO_RUNTIME: &str = include_str!("lib.deno_runtime.d.ts"); - -/// Same as deno_typescript::get_asset but also has lib.deno_runtime.d.ts -pub fn get_asset(name: &str) -> Option<&'static str> { - match name { - "lib.deno_runtime.d.ts" => Some(DENO_RUNTIME), - _ => deno_typescript::get_asset(name), - } -} - -#[test] -fn cli_snapshot() { - let mut isolate = - deno::Isolate::new(deno::StartupData::Snapshot(CLI_SNAPSHOT), false); - deno::js_check(isolate.execute( - "<anon>", - r#" - if (!window) { - throw Error("bad"); - } - console.log("we have console.log!!!"); - "#, - )); -} - -#[test] -fn compiler_snapshot() { - let mut isolate = - deno::Isolate::new(deno::StartupData::Snapshot(COMPILER_SNAPSHOT), false); - deno::js_check(isolate.execute( - "<anon>", - r#" - if (!compilerMain) { - throw Error("bad"); - } - console.log(`ts version: ${ts.version}`); - "#, - )); -} diff --git a/js/lib.web_assembly.d.ts b/js/lib.web_assembly.d.ts deleted file mode 100644 index 8c357840a..000000000 --- a/js/lib.web_assembly.d.ts +++ /dev/null @@ -1,173 +0,0 @@ -// This follows the WebIDL at: https://webassembly.github.io/spec/js-api/ -// And follow on WebIDL at: https://webassembly.github.io/spec/web-api/ - -/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any */ - -declare namespace WebAssembly { - interface WebAssemblyInstantiatedSource { - module: Module; - instance: Instance; - } - - /** Compiles a `WebAssembly.Module` from WebAssembly binary code. This - * function is useful if it is necessary to a compile a module before it can - * be instantiated (otherwise, the `WebAssembly.instantiate()` function - * should be used). */ - function compile(bufferSource: domTypes.BufferSource): Promise<Module>; - - /** Compiles a `WebAssembly.Module` directly from a streamed underlying - * source. This function is useful if it is necessary to a compile a module - * before it can be instantiated (otherwise, the - * `WebAssembly.instantiateStreaming()` function should be used). */ - function compileStreaming( - source: Promise<domTypes.Response> - ): Promise<Module>; - - /** Takes the WebAssembly binary code, in the form of a typed array or - * `ArrayBuffer`, and performs both compilation and instantiation in one step. - * The returned `Promise` resolves to both a compiled `WebAssembly.Module` and - * its first `WebAssembly.Instance`. */ - function instantiate( - bufferSource: domTypes.BufferSource, - importObject?: object - ): Promise<WebAssemblyInstantiatedSource>; - - /** Takes an already-compiled `WebAssembly.Module` and returns a `Promise` - * that resolves to an `Instance` of that `Module`. This overload is useful if - * the `Module` has already been compiled. */ - function instantiate( - module: Module, - importObject?: object - ): Promise<Instance>; - - /** Compiles and instantiates a WebAssembly module directly from a streamed - * underlying source. This is the most efficient, optimized way to load wasm - * code. */ - function instantiateStreaming( - source: Promise<domTypes.Response>, - importObject?: object - ): Promise<WebAssemblyInstantiatedSource>; - - /** Validates a given typed array of WebAssembly binary code, returning - * whether the bytes form a valid wasm module (`true`) or not (`false`). */ - function validate(bufferSource: domTypes.BufferSource): boolean; - - type ImportExportKind = "function" | "table" | "memory" | "global"; - - interface ModuleExportDescriptor { - name: string; - kind: ImportExportKind; - } - interface ModuleImportDescriptor { - module: string; - name: string; - kind: ImportExportKind; - } - - class Module { - constructor(bufferSource: domTypes.BufferSource); - - /** Given a `Module` and string, returns a copy of the contents of all - * custom sections in the module with the given string name. */ - static customSections( - moduleObject: Module, - sectionName: string - ): ArrayBuffer; - - /** Given a `Module`, returns an array containing descriptions of all the - * declared exports. */ - static exports(moduleObject: Module): ModuleExportDescriptor[]; - - /** Given a `Module`, returns an array containing descriptions of all the - * declared imports. */ - static imports(moduleObject: Module): ModuleImportDescriptor[]; - } - - class Instance<T extends object = { [key: string]: any }> { - constructor(module: Module, importObject?: object); - - /** An object containing as its members all the functions exported from the - * WebAssembly module instance, to allow them to be accessed and used by - * JavaScript. */ - readonly exports: T; - } - - interface MemoryDescriptor { - initial: number; - maximum?: number; - } - - class Memory { - constructor(descriptor: MemoryDescriptor); - - /** An accessor property that returns the buffer contained in the memory. */ - readonly buffer: ArrayBuffer; - - /** Increases the size of the memory instance by a specified number of - * WebAssembly pages (each one is 64KB in size). */ - grow(delta: number): number; - } - - type TableKind = "anyfunc"; - - interface TableDescriptor { - element: TableKind; - initial: number; - maximum?: number; - } - - class Table { - constructor(descriptor: TableDescriptor); - - /** Returns the length of the table, i.e. the number of elements. */ - readonly length: number; - - /** Accessor function — gets the element stored at a given index. */ - get(index: number): (...args: any[]) => any; - - /** Increases the size of the Table instance by a specified number of - * elements. */ - grow(delta: number): number; - - /** Sets an element stored at a given index to a given value. */ - set(index: number, value: (...args: any[]) => any): void; - } - - interface GlobalDescriptor { - value: string; - mutable?: boolean; - } - - /** Represents a global variable instance, accessible from both JavaScript and - * importable/exportable across one or more `WebAssembly.Module` instances. - * This allows dynamic linking of multiple modules. */ - class Global { - constructor(descriptor: GlobalDescriptor, value?: any); - - /** Old-style method that returns the value contained inside the global - * variable. */ - valueOf(): any; - - /** The value contained inside the global variable — this can be used to - * directly set and get the global's value. */ - value: any; - } - - /** Indicates an error during WebAssembly decoding or validation */ - class CompileError extends Error { - constructor(message: string, fileName?: string, lineNumber?: string); - } - - /** Indicates an error during module instantiation (besides traps from the - * start function). */ - class LinkError extends Error { - constructor(message: string, fileName?: string, lineNumber?: string); - } - - /** Is thrown whenever WebAssembly specifies a trap. */ - class RuntimeError extends Error { - constructor(message: string, fileName?: string, lineNumber?: string); - } -} - -/* eslint-enable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any */ diff --git a/js/link.ts b/js/link.ts deleted file mode 100644 index a6f732926..000000000 --- a/js/link.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendSync, sendAsync } from "./dispatch_json.ts"; -import * as dispatch from "./dispatch.ts"; - -/** Synchronously creates `newname` as a hard link to `oldname`. - * - * Deno.linkSync("old/name", "new/name"); - */ -export function linkSync(oldname: string, newname: string): void { - sendSync(dispatch.OP_LINK, { oldname, newname }); -} - -/** Creates `newname` as a hard link to `oldname`. - * - * await Deno.link("old/name", "new/name"); - */ -export async function link(oldname: string, newname: string): Promise<void> { - await sendAsync(dispatch.OP_LINK, { oldname, newname }); -} diff --git a/js/link_test.ts b/js/link_test.ts deleted file mode 100644 index 9425e6eab..000000000 --- a/js/link_test.ts +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { testPerm, assert, assertEquals } from "./test_util.ts"; - -testPerm({ read: true, write: true }, function linkSyncSuccess(): void { - const testDir = Deno.makeTempDirSync(); - const oldData = "Hardlink"; - const oldName = testDir + "/oldname"; - const newName = testDir + "/newname"; - Deno.writeFileSync(oldName, new TextEncoder().encode(oldData)); - // Create the hard link. - Deno.linkSync(oldName, newName); - // We should expect reading the same content. - const newData = new TextDecoder().decode(Deno.readFileSync(newName)); - assertEquals(oldData, newData); - // Writing to newname also affects oldname. - const newData2 = "Modified"; - Deno.writeFileSync(newName, new TextEncoder().encode(newData2)); - assertEquals(newData2, new TextDecoder().decode(Deno.readFileSync(oldName))); - // Writing to oldname also affects newname. - const newData3 = "ModifiedAgain"; - Deno.writeFileSync(oldName, new TextEncoder().encode(newData3)); - assertEquals(newData3, new TextDecoder().decode(Deno.readFileSync(newName))); - // Remove oldname. File still accessible through newname. - Deno.removeSync(oldName); - const newNameStat = Deno.statSync(newName); - assert(newNameStat.isFile()); - assert(!newNameStat.isSymlink()); // Not a symlink. - assertEquals(newData3, new TextDecoder().decode(Deno.readFileSync(newName))); -}); - -testPerm({ read: true, write: true }, function linkSyncExists(): void { - const testDir = Deno.makeTempDirSync(); - const oldName = testDir + "/oldname"; - const newName = testDir + "/newname"; - Deno.writeFileSync(oldName, new TextEncoder().encode("oldName")); - // newname is already created. - Deno.writeFileSync(newName, new TextEncoder().encode("newName")); - - let err; - try { - Deno.linkSync(oldName, newName); - } catch (e) { - err = e; - } - assert(!!err); - assertEquals(err.kind, Deno.ErrorKind.AlreadyExists); - assertEquals(err.name, "AlreadyExists"); -}); - -testPerm({ read: true, write: true }, function linkSyncNotFound(): void { - const testDir = Deno.makeTempDirSync(); - const oldName = testDir + "/oldname"; - const newName = testDir + "/newname"; - - let err; - try { - Deno.linkSync(oldName, newName); - } catch (e) { - err = e; - } - assert(!!err); - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); - -testPerm({ read: false, write: true }, function linkSyncReadPerm(): void { - let err; - try { - Deno.linkSync("oldbaddir", "newbaddir"); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -testPerm({ read: true, write: false }, function linkSyncWritePerm(): void { - let err; - try { - Deno.linkSync("oldbaddir", "newbaddir"); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -testPerm({ read: true, write: true }, async function linkSuccess(): Promise< - void -> { - const testDir = Deno.makeTempDirSync(); - const oldData = "Hardlink"; - const oldName = testDir + "/oldname"; - const newName = testDir + "/newname"; - Deno.writeFileSync(oldName, new TextEncoder().encode(oldData)); - // Create the hard link. - await Deno.link(oldName, newName); - // We should expect reading the same content. - const newData = new TextDecoder().decode(Deno.readFileSync(newName)); - assertEquals(oldData, newData); - // Writing to newname also affects oldname. - const newData2 = "Modified"; - Deno.writeFileSync(newName, new TextEncoder().encode(newData2)); - assertEquals(newData2, new TextDecoder().decode(Deno.readFileSync(oldName))); - // Writing to oldname also affects newname. - const newData3 = "ModifiedAgain"; - Deno.writeFileSync(oldName, new TextEncoder().encode(newData3)); - assertEquals(newData3, new TextDecoder().decode(Deno.readFileSync(newName))); - // Remove oldname. File still accessible through newname. - Deno.removeSync(oldName); - const newNameStat = Deno.statSync(newName); - assert(newNameStat.isFile()); - assert(!newNameStat.isSymlink()); // Not a symlink. - assertEquals(newData3, new TextDecoder().decode(Deno.readFileSync(newName))); -}); diff --git a/js/location.ts b/js/location.ts deleted file mode 100644 index d495f99ca..000000000 --- a/js/location.ts +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { URL } from "./url.ts"; -import { notImplemented } from "./util.ts"; -import { Location } from "./dom_types.ts"; -import { window } from "./window.ts"; - -export class LocationImpl implements Location { - constructor(url: string) { - const u = new URL(url); - this.url = u; - this.hash = u.hash; - this.host = u.host; - this.href = u.href; - this.hostname = u.hostname; - this.origin = u.protocol + "//" + u.host; - this.pathname = u.pathname; - this.protocol = u.protocol; - this.port = u.port; - this.search = u.search; - } - - private url: URL; - - toString(): string { - return this.url.toString(); - } - - readonly ancestorOrigins: string[] = []; - hash: string; - host: string; - hostname: string; - href: string; - readonly origin: string; - pathname: string; - port: string; - protocol: string; - search: string; - assign(_url: string): void { - throw notImplemented(); - } - reload(): void { - throw notImplemented(); - } - replace(_url: string): void { - throw notImplemented(); - } -} - -export function setLocation(url: string): void { - window.location = new LocationImpl(url); - Object.freeze(window.location); -} diff --git a/js/location_test.ts b/js/location_test.ts deleted file mode 100644 index c8daab16d..000000000 --- a/js/location_test.ts +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, assert } from "./test_util.ts"; - -test(function locationBasic(): void { - // location example: file:///Users/rld/src/deno/js/unit_tests.ts - console.log("location", window.location.toString()); - assert(window.location.toString().endsWith("unit_tests.ts")); -}); diff --git a/js/main.ts b/js/main.ts deleted file mode 100644 index 09e7ce453..000000000 --- a/js/main.ts +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import "./globals.ts"; - -import { assert, log } from "./util.ts"; -import * as os from "./os.ts"; -import { args } from "./deno.ts"; -import { setPrepareStackTrace } from "./error_stack.ts"; -import { replLoop } from "./repl.ts"; -import { setVersions } from "./version.ts"; -import { window } from "./window.ts"; -import { setLocation } from "./location.ts"; -import { setBuildInfo } from "./build.ts"; -import { setSignals } from "./process.ts"; - -function denoMain(preserveDenoNamespace = true, name?: string): void { - const s = os.start(preserveDenoNamespace, name); - - setBuildInfo(s.os, s.arch); - setSignals(); - setVersions(s.denoVersion, s.v8Version, s.tsVersion); - - setPrepareStackTrace(Error); - - if (s.mainModule) { - assert(s.mainModule.length > 0); - setLocation(s.mainModule); - } - - log("cwd", s.cwd); - - for (let i = 1; i < s.argv.length; i++) { - args.push(s.argv[i]); - } - log("args", args); - Object.freeze(args); - - if (!s.mainModule) { - replLoop(); - } -} -window["denoMain"] = denoMain; diff --git a/js/make_temp_dir.ts b/js/make_temp_dir.ts deleted file mode 100644 index 14494b5da..000000000 --- a/js/make_temp_dir.ts +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendSync, sendAsync } from "./dispatch_json.ts"; -import * as dispatch from "./dispatch.ts"; - -export interface MakeTempDirOptions { - dir?: string; - prefix?: string; - suffix?: string; -} - -/** makeTempDirSync is the synchronous version of `makeTempDir`. - * - * const tempDirName0 = Deno.makeTempDirSync(); - * const tempDirName1 = Deno.makeTempDirSync({ prefix: 'my_temp' }); - */ -export function makeTempDirSync(options: MakeTempDirOptions = {}): string { - return sendSync(dispatch.OP_MAKE_TEMP_DIR, options); -} - -/** makeTempDir creates a new temporary directory in the directory `dir`, its - * name beginning with `prefix` and ending with `suffix`. - * It returns the full path to the newly created directory. - * If `dir` is unspecified, tempDir uses the default directory for temporary - * files. Multiple programs calling tempDir simultaneously will not choose the - * same directory. It is the caller's responsibility to remove the directory - * when no longer needed. - * - * const tempDirName0 = await Deno.makeTempDir(); - * const tempDirName1 = await Deno.makeTempDir({ prefix: 'my_temp' }); - */ -export async function makeTempDir( - options: MakeTempDirOptions = {} -): Promise<string> { - return await sendAsync(dispatch.OP_MAKE_TEMP_DIR, options); -} diff --git a/js/make_temp_dir_test.ts b/js/make_temp_dir_test.ts deleted file mode 100644 index aa44b65c5..000000000 --- a/js/make_temp_dir_test.ts +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, testPerm, assert, assertEquals } from "./test_util.ts"; - -testPerm({ write: true }, function makeTempDirSyncSuccess(): void { - const dir1 = Deno.makeTempDirSync({ prefix: "hello", suffix: "world" }); - const dir2 = Deno.makeTempDirSync({ prefix: "hello", suffix: "world" }); - // Check that both dirs are different. - assert(dir1 !== dir2); - for (const dir of [dir1, dir2]) { - // Check that the prefix and suffix are applied. - const lastPart = dir.replace(/^.*[\\\/]/, ""); - assert(lastPart.startsWith("hello")); - assert(lastPart.endsWith("world")); - } - // Check that the `dir` option works. - const dir3 = Deno.makeTempDirSync({ dir: dir1 }); - assert(dir3.startsWith(dir1)); - assert(/^[\\\/]/.test(dir3.slice(dir1.length))); - // Check that creating a temp dir inside a nonexisting directory fails. - let err; - try { - Deno.makeTempDirSync({ dir: "/baddir" }); - } catch (err_) { - err = err_; - } - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); - -test(function makeTempDirSyncPerm(): void { - // makeTempDirSync should require write permissions (for now). - let err; - try { - Deno.makeTempDirSync({ dir: "/baddir" }); - } catch (err_) { - err = err_; - } - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -testPerm({ write: true }, async function makeTempDirSuccess(): Promise<void> { - const dir1 = await Deno.makeTempDir({ prefix: "hello", suffix: "world" }); - const dir2 = await Deno.makeTempDir({ prefix: "hello", suffix: "world" }); - // Check that both dirs are different. - assert(dir1 !== dir2); - for (const dir of [dir1, dir2]) { - // Check that the prefix and suffix are applied. - const lastPart = dir.replace(/^.*[\\\/]/, ""); - assert(lastPart.startsWith("hello")); - assert(lastPart.endsWith("world")); - } - // Check that the `dir` option works. - const dir3 = await Deno.makeTempDir({ dir: dir1 }); - assert(dir3.startsWith(dir1)); - assert(/^[\\\/]/.test(dir3.slice(dir1.length))); - // Check that creating a temp dir inside a nonexisting directory fails. - let err; - try { - await Deno.makeTempDir({ dir: "/baddir" }); - } catch (err_) { - err = err_; - } - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); diff --git a/js/metrics.ts b/js/metrics.ts deleted file mode 100644 index b32c29789..000000000 --- a/js/metrics.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as dispatch from "./dispatch.ts"; -import { sendSync } from "./dispatch_json.ts"; - -export interface Metrics { - opsDispatched: number; - opsCompleted: number; - bytesSentControl: number; - bytesSentData: number; - bytesReceived: number; -} - -/** Receive metrics from the privileged side of Deno. - * - * > console.table(Deno.metrics()) - * ┌──────────────────┬────────┐ - * │ (index) │ Values │ - * ├──────────────────┼────────┤ - * │ opsDispatched │ 9 │ - * │ opsCompleted │ 9 │ - * │ bytesSentControl │ 504 │ - * │ bytesSentData │ 0 │ - * │ bytesReceived │ 856 │ - * └──────────────────┴────────┘ - */ -export function metrics(): Metrics { - return sendSync(dispatch.OP_METRICS); -} diff --git a/js/metrics_test.ts b/js/metrics_test.ts deleted file mode 100644 index de41a0cb1..000000000 --- a/js/metrics_test.ts +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, testPerm, assert } from "./test_util.ts"; - -test(async function metrics(): Promise<void> { - const m1 = Deno.metrics(); - assert(m1.opsDispatched > 0); - assert(m1.opsCompleted > 0); - assert(m1.bytesSentControl > 0); - assert(m1.bytesSentData >= 0); - assert(m1.bytesReceived > 0); - - // Write to stdout to ensure a "data" message gets sent instead of just - // control messages. - const dataMsg = new Uint8Array([41, 42, 43]); - await Deno.stdout.write(dataMsg); - - const m2 = Deno.metrics(); - assert(m2.opsDispatched > m1.opsDispatched); - assert(m2.opsCompleted > m1.opsCompleted); - assert(m2.bytesSentControl > m1.bytesSentControl); - assert(m2.bytesSentData >= m1.bytesSentData + dataMsg.byteLength); - assert(m2.bytesReceived > m1.bytesReceived); -}); - -testPerm({ write: true }, function metricsUpdatedIfNoResponseSync(): void { - const filename = Deno.makeTempDirSync() + "/test.txt"; - - const data = new Uint8Array([41, 42, 43]); - Deno.writeFileSync(filename, data, { perm: 0o666 }); - - const metrics = Deno.metrics(); - assert(metrics.opsDispatched === metrics.opsCompleted); -}); - -testPerm( - { write: true }, - async function metricsUpdatedIfNoResponseAsync(): Promise<void> { - const filename = Deno.makeTempDirSync() + "/test.txt"; - - const data = new Uint8Array([41, 42, 43]); - await Deno.writeFile(filename, data, { perm: 0o666 }); - - const metrics = Deno.metrics(); - assert(metrics.opsDispatched === metrics.opsCompleted); - } -); diff --git a/js/mixins/dom_iterable.ts b/js/mixins/dom_iterable.ts deleted file mode 100644 index bbd1905ce..000000000 --- a/js/mixins/dom_iterable.ts +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -// eslint-disable-next-line @typescript-eslint/no-unused-vars -import { DomIterable } from "../dom_types.ts"; -import { window } from "../window.ts"; -import { requiredArguments } from "../util.ts"; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type Constructor<T = {}> = new (...args: any[]) => T; - -/** Mixes in a DOM iterable methods into a base class, assumes that there is - * a private data iterable that is part of the base class, located at - * `[dataSymbol]`. - * TODO Don't expose DomIterableMixin from "deno" namespace. - */ -export function DomIterableMixin<K, V, TBase extends Constructor>( - Base: TBase, - dataSymbol: symbol -): TBase & Constructor<DomIterable<K, V>> { - // we have to cast `this` as `any` because there is no way to describe the - // Base class in a way where the Symbol `dataSymbol` is defined. So the - // runtime code works, but we do lose a little bit of type safety. - - // Additionally, we have to not use .keys() nor .values() since the internal - // slot differs in type - some have a Map, which yields [K, V] in - // Symbol.iterator, and some have an Array, which yields V, in this case - // [K, V] too as they are arrays of tuples. - - const DomIterable = class extends Base { - *entries(): IterableIterator<[K, V]> { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - for (const entry of (this as any)[dataSymbol]) { - yield entry; - } - } - - *keys(): IterableIterator<K> { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - for (const [key] of (this as any)[dataSymbol]) { - yield key; - } - } - - *values(): IterableIterator<V> { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - for (const [, value] of (this as any)[dataSymbol]) { - yield value; - } - } - - forEach( - callbackfn: (value: V, key: K, parent: this) => void, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - thisArg?: any - ): void { - requiredArguments( - `${this.constructor.name}.forEach`, - arguments.length, - 1 - ); - callbackfn = callbackfn.bind(thisArg == null ? window : Object(thisArg)); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - for (const [key, value] of (this as any)[dataSymbol]) { - callbackfn(value, key, this); - } - } - - *[Symbol.iterator](): IterableIterator<[K, V]> { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - for (const entry of (this as any)[dataSymbol]) { - yield entry; - } - } - }; - - // we want the Base class name to be the name of the class. - Object.defineProperty(DomIterable, "name", { - value: Base.name, - configurable: true - }); - - return DomIterable; -} diff --git a/js/mixins/dom_iterable_test.ts b/js/mixins/dom_iterable_test.ts deleted file mode 100644 index 4c84fa68e..000000000 --- a/js/mixins/dom_iterable_test.ts +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, assert, assertEquals } from "../test_util.ts"; - -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -function setup() { - const dataSymbol = Symbol("data symbol"); - class Base { - private [dataSymbol] = new Map<string, number>(); - - constructor( - data: Array<[string, number]> | IterableIterator<[string, number]> - ) { - for (const [key, value] of data) { - this[dataSymbol].set(key, value); - } - } - } - - return { - Base, - // This is using an internal API we don't want published as types, so having - // to cast to any to "trick" TypeScript - // eslint-disable-next-line @typescript-eslint/no-explicit-any - DomIterable: (Deno as any).DomIterableMixin(Base, dataSymbol) - }; -} - -test(function testDomIterable(): void { - const { DomIterable, Base } = setup(); - - const fixture: Array<[string, number]> = [["foo", 1], ["bar", 2]]; - - const domIterable = new DomIterable(fixture); - - assertEquals(Array.from(domIterable.entries()), fixture); - assertEquals(Array.from(domIterable.values()), [1, 2]); - assertEquals(Array.from(domIterable.keys()), ["foo", "bar"]); - - let result: Array<[string, number]> = []; - for (const [key, value] of domIterable) { - assert(key != null); - assert(value != null); - result.push([key, value]); - } - assertEquals(fixture, result); - - result = []; - const scope = {}; - function callback(value, key, parent): void { - assertEquals(parent, domIterable); - assert(key != null); - assert(value != null); - assert(this === scope); - result.push([key, value]); - } - domIterable.forEach(callback, scope); - assertEquals(fixture, result); - - assertEquals(DomIterable.name, Base.name); -}); - -test(function testDomIterableScope(): void { - const { DomIterable } = setup(); - - const domIterable = new DomIterable([["foo", 1]]); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - function checkScope(thisArg: any, expected: any): void { - function callback(): void { - assertEquals(this, expected); - } - domIterable.forEach(callback, thisArg); - } - - checkScope(0, Object(0)); - checkScope("", Object("")); - checkScope(null, window); - checkScope(undefined, window); -}); diff --git a/js/mkdir.ts b/js/mkdir.ts deleted file mode 100644 index bc09ba358..000000000 --- a/js/mkdir.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendSync, sendAsync } from "./dispatch_json.ts"; -import * as dispatch from "./dispatch.ts"; - -/** Creates a new directory with the specified path synchronously. - * If `recursive` is set to true, nested directories will be created (also known - * as "mkdir -p"). - * `mode` sets permission bits (before umask) on UNIX and does nothing on - * Windows. - * - * Deno.mkdirSync("new_dir"); - * Deno.mkdirSync("nested/directories", true); - */ -export function mkdirSync(path: string, recursive = false, mode = 0o777): void { - sendSync(dispatch.OP_MKDIR, { path, recursive, mode }); -} - -/** Creates a new directory with the specified path. - * If `recursive` is set to true, nested directories will be created (also known - * as "mkdir -p"). - * `mode` sets permission bits (before umask) on UNIX and does nothing on - * Windows. - * - * await Deno.mkdir("new_dir"); - * await Deno.mkdir("nested/directories", true); - */ -export async function mkdir( - path: string, - recursive = false, - mode = 0o777 -): Promise<void> { - await sendAsync(dispatch.OP_MKDIR, { path, recursive, mode }); -} diff --git a/js/mkdir_test.ts b/js/mkdir_test.ts deleted file mode 100644 index 9e97265f0..000000000 --- a/js/mkdir_test.ts +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { testPerm, assert, assertEquals } from "./test_util.ts"; - -testPerm({ read: true, write: true }, function mkdirSyncSuccess(): void { - const path = Deno.makeTempDirSync() + "/dir"; - Deno.mkdirSync(path); - const pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory()); -}); - -testPerm({ read: true, write: true }, function mkdirSyncMode(): void { - const path = Deno.makeTempDirSync() + "/dir"; - Deno.mkdirSync(path, false, 0o755); // no perm for x - const pathInfo = Deno.statSync(path); - if (pathInfo.mode !== null) { - // Skip windows - assertEquals(pathInfo.mode & 0o777, 0o755); - } -}); - -testPerm({ write: false }, function mkdirSyncPerm(): void { - let err; - try { - Deno.mkdirSync("/baddir"); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -testPerm({ read: true, write: true }, async function mkdirSuccess(): Promise< - void -> { - const path = Deno.makeTempDirSync() + "/dir"; - await Deno.mkdir(path); - const pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory()); -}); - -testPerm({ write: true }, function mkdirErrIfExists(): void { - let err; - try { - Deno.mkdirSync("."); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.AlreadyExists); - assertEquals(err.name, "AlreadyExists"); -}); - -testPerm({ read: true, write: true }, function mkdirSyncRecursive(): void { - const path = Deno.makeTempDirSync() + "/nested/directory"; - Deno.mkdirSync(path, true); - const pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory()); -}); - -testPerm({ read: true, write: true }, async function mkdirRecursive(): Promise< - void -> { - const path = Deno.makeTempDirSync() + "/nested/directory"; - await Deno.mkdir(path, true); - const pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory()); -}); diff --git a/js/mock_builtin.js b/js/mock_builtin.js deleted file mode 100644 index 9c6730d69..000000000 --- a/js/mock_builtin.js +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -export default undefined; diff --git a/js/net.ts b/js/net.ts deleted file mode 100644 index a7ad2b73c..000000000 --- a/js/net.ts +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { EOF, Reader, Writer, Closer } from "./io.ts"; -import { notImplemented } from "./util.ts"; -import { read, write, close } from "./files.ts"; -import * as dispatch from "./dispatch.ts"; -import { sendSync, sendAsync } from "./dispatch_json.ts"; - -export type Transport = "tcp"; -// TODO support other types: -// export type Transport = "tcp" | "tcp4" | "tcp6" | "unix" | "unixpacket"; - -// TODO(ry) Replace 'address' with 'hostname' and 'port', similar to DialOptions -// and ListenOptions. -export interface Addr { - transport: Transport; - address: string; -} - -/** A Listener is a generic transport listener for stream-oriented protocols. */ -export interface Listener extends AsyncIterator<Conn> { - /** Waits for and resolves to the next connection to the `Listener`. */ - accept(): Promise<Conn>; - - /** Close closes the listener. Any pending accept promises will be rejected - * with errors. - */ - close(): void; - - /** Return the address of the `Listener`. */ - addr(): Addr; - - [Symbol.asyncIterator](): AsyncIterator<Conn>; -} - -enum ShutdownMode { - // See http://man7.org/linux/man-pages/man2/shutdown.2.html - // Corresponding to SHUT_RD, SHUT_WR, SHUT_RDWR - Read = 0, - Write, - ReadWrite // unused -} - -function shutdown(rid: number, how: ShutdownMode): void { - sendSync(dispatch.OP_SHUTDOWN, { rid, how }); -} - -export class ConnImpl implements Conn { - constructor( - readonly rid: number, - readonly remoteAddr: string, - readonly localAddr: string - ) {} - - write(p: Uint8Array): Promise<number> { - return write(this.rid, p); - } - - read(p: Uint8Array): Promise<number | EOF> { - return read(this.rid, p); - } - - close(): void { - close(this.rid); - } - - /** closeRead shuts down (shutdown(2)) the reading side of the TCP connection. - * Most callers should just use close(). - */ - closeRead(): void { - shutdown(this.rid, ShutdownMode.Read); - } - - /** closeWrite shuts down (shutdown(2)) the writing side of the TCP - * connection. Most callers should just use close(). - */ - closeWrite(): void { - shutdown(this.rid, ShutdownMode.Write); - } -} - -class ListenerImpl implements Listener { - constructor( - readonly rid: number, - private transport: Transport, - private localAddr: string - ) {} - - async accept(): Promise<Conn> { - const res = await sendAsync(dispatch.OP_ACCEPT, { rid: this.rid }); - return new ConnImpl(res.rid, res.remoteAddr, res.localAddr); - } - - close(): void { - close(this.rid); - } - - addr(): Addr { - return { - transport: this.transport, - address: this.localAddr - }; - } - - async next(): Promise<IteratorResult<Conn>> { - return { - done: false, - value: await this.accept() - }; - } - - [Symbol.asyncIterator](): AsyncIterator<Conn> { - return this; - } -} - -export interface Conn extends Reader, Writer, Closer { - /** The local address of the connection. */ - localAddr: string; - /** The remote address of the connection. */ - remoteAddr: string; - /** The resource ID of the connection. */ - rid: number; - /** Shuts down (`shutdown(2)`) the reading side of the TCP connection. Most - * callers should just use `close()`. - */ - closeRead(): void; - /** Shuts down (`shutdown(2)`) the writing side of the TCP connection. Most - * callers should just use `close()`. - */ - closeWrite(): void; -} - -export interface ListenOptions { - port: number; - hostname?: string; - transport?: Transport; -} - -/** Listen announces on the local transport address. - * - * @param options - * @param options.port The port to connect to. (Required.) - * @param options.hostname A literal IP address or host name that can be - * resolved to an IP address. If not specified, defaults to 0.0.0.0 - * @param options.transport Defaults to "tcp". Later we plan to add "tcp4", - * "tcp6", "udp", "udp4", "udp6", "ip", "ip4", "ip6", "unix", "unixgram" and - * "unixpacket". - * - * Examples: - * - * listen({ port: 80 }) - * listen({ hostname: "192.0.2.1", port: 80 }) - * listen({ hostname: "[2001:db8::1]", port: 80 }); - * listen({ hostname: "golang.org", port: 80, transport: "tcp" }) - */ -export function listen(options: ListenOptions): Listener { - const hostname = options.hostname || "0.0.0.0"; - const transport = options.transport || "tcp"; - const res = sendSync(dispatch.OP_LISTEN, { - hostname, - port: options.port, - transport - }); - return new ListenerImpl(res.rid, transport, res.localAddr); -} - -export interface DialOptions { - port: number; - hostname?: string; - transport?: Transport; -} - -/** Dial connects to the address on the named transport. - * - * @param options - * @param options.port The port to connect to. (Required.) - * @param options.hostname A literal IP address or host name that can be - * resolved to an IP address. If not specified, defaults to 127.0.0.1 - * @param options.transport Defaults to "tcp". Later we plan to add "tcp4", - * "tcp6", "udp", "udp4", "udp6", "ip", "ip4", "ip6", "unix", "unixgram" and - * "unixpacket". - * - * Examples: - * - * dial({ port: 80 }) - * dial({ hostname: "192.0.2.1", port: 80 }) - * dial({ hostname: "[2001:db8::1]", port: 80 }); - * dial({ hostname: "golang.org", port: 80, transport: "tcp" }) - */ -export async function dial(options: DialOptions): Promise<Conn> { - const res = await sendAsync(dispatch.OP_DIAL, { - hostname: options.hostname || "127.0.0.1", - port: options.port, - transport: options.transport || "tcp" - }); - return new ConnImpl(res.rid, res.remoteAddr!, res.localAddr!); -} - -/** **RESERVED** */ -export async function connect( - _transport: Transport, - _address: string -): Promise<Conn> { - return notImplemented(); -} diff --git a/js/net_test.ts b/js/net_test.ts deleted file mode 100644 index 33f4f7d07..000000000 --- a/js/net_test.ts +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { testPerm, assert, assertEquals } from "./test_util.ts"; - -testPerm({ net: true }, function netListenClose(): void { - const listener = Deno.listen({ hostname: "127.0.0.1", port: 4500 }); - const addr = listener.addr(); - assertEquals(addr.transport, "tcp"); - // TODO(ry) Replace 'address' with 'hostname' and 'port', similar to - // DialOptions and ListenOptions. - assertEquals(addr.address, "127.0.0.1:4500"); - listener.close(); -}); - -testPerm({ net: true }, async function netCloseWhileAccept(): Promise<void> { - const listener = Deno.listen({ port: 4501 }); - const p = listener.accept(); - listener.close(); - let err; - try { - await p; - } catch (e) { - err = e; - } - assert(!!err); - assertEquals(err.kind, Deno.ErrorKind.Other); - assertEquals(err.message, "Listener has been closed"); -}); - -testPerm({ net: true }, async function netConcurrentAccept(): Promise<void> { - const listener = Deno.listen({ port: 4502 }); - let acceptErrCount = 0; - const checkErr = (e): void => { - assertEquals(e.kind, Deno.ErrorKind.Other); - if (e.message === "Listener has been closed") { - assertEquals(acceptErrCount, 1); - } else if (e.message === "Another accept task is ongoing") { - acceptErrCount++; - } else { - throw new Error("Unexpected error message"); - } - }; - const p = listener.accept().catch(checkErr); - const p1 = listener.accept().catch(checkErr); - await Promise.race([p, p1]); - listener.close(); - await [p, p1]; - assertEquals(acceptErrCount, 1); -}); - -testPerm({ net: true }, async function netDialListen(): Promise<void> { - const listener = Deno.listen({ port: 4500 }); - listener.accept().then( - async (conn): Promise<void> => { - assert(conn.remoteAddr != null); - assertEquals(conn.localAddr, "127.0.0.1:4500"); - await conn.write(new Uint8Array([1, 2, 3])); - conn.close(); - } - ); - const conn = await Deno.dial({ hostname: "127.0.0.1", port: 4500 }); - assertEquals(conn.remoteAddr, "127.0.0.1:4500"); - assert(conn.localAddr != null); - const buf = new Uint8Array(1024); - const readResult = await conn.read(buf); - assertEquals(3, readResult); - assertEquals(1, buf[0]); - assertEquals(2, buf[1]); - assertEquals(3, buf[2]); - assert(conn.rid > 0); - - assert(readResult !== Deno.EOF); - - const readResult2 = await conn.read(buf); - assertEquals(Deno.EOF, readResult2); - - listener.close(); - conn.close(); -}); - -/* TODO(ry) Re-enable this test. -testPerm({ net: true }, async function netListenAsyncIterator(): Promise<void> { - const listener = Deno.listen(":4500"); - const runAsyncIterator = async (): Promise<void> => { - for await (let conn of listener) { - await conn.write(new Uint8Array([1, 2, 3])); - conn.close(); - } - }; - runAsyncIterator(); - const conn = await Deno.dial("127.0.0.1:4500"); - const buf = new Uint8Array(1024); - const readResult = await conn.read(buf); - assertEquals(3, readResult); - assertEquals(1, buf[0]); - assertEquals(2, buf[1]); - assertEquals(3, buf[2]); - assert(conn.rid > 0); - - assert(readResult !== Deno.EOF); - - const readResult2 = await conn.read(buf); - assertEquals(Deno.EOF, readResult2); - - listener.close(); - conn.close(); -}); - */ - -/* TODO Fix broken test. -testPerm({ net: true }, async function netCloseReadSuccess() { - const addr = "127.0.0.1:4500"; - const listener = Deno.listen(addr); - const closeDeferred = deferred(); - const closeReadDeferred = deferred(); - listener.accept().then(async conn => { - await closeReadDeferred.promise; - await conn.write(new Uint8Array([1, 2, 3])); - const buf = new Uint8Array(1024); - const readResult = await conn.read(buf); - assertEquals(3, readResult); - assertEquals(4, buf[0]); - assertEquals(5, buf[1]); - assertEquals(6, buf[2]); - conn.close(); - closeDeferred.resolve(); - }); - const conn = await Deno.dial(addr); - conn.closeRead(); // closing read - closeReadDeferred.resolve(); - const buf = new Uint8Array(1024); - const readResult = await conn.read(buf); - assertEquals(Deno.EOF, readResult); // with immediate EOF - // Ensure closeRead does not impact write - await conn.write(new Uint8Array([4, 5, 6])); - await closeDeferred.promise; - listener.close(); - conn.close(); -}); -*/ - -/* TODO Fix broken test. -testPerm({ net: true }, async function netDoubleCloseRead() { - const addr = "127.0.0.1:4500"; - const listener = Deno.listen(addr); - const closeDeferred = deferred(); - listener.accept().then(async conn => { - await conn.write(new Uint8Array([1, 2, 3])); - await closeDeferred.promise; - conn.close(); - }); - const conn = await Deno.dial(addr); - conn.closeRead(); // closing read - let err; - try { - // Duplicated close should throw error - conn.closeRead(); - } catch (e) { - err = e; - } - assert(!!err); - assertEquals(err.kind, Deno.ErrorKind.NotConnected); - assertEquals(err.name, "NotConnected"); - closeDeferred.resolve(); - listener.close(); - conn.close(); -}); -*/ - -/* TODO Fix broken test. -testPerm({ net: true }, async function netCloseWriteSuccess() { - const addr = "127.0.0.1:4500"; - const listener = Deno.listen(addr); - const closeDeferred = deferred(); - listener.accept().then(async conn => { - await conn.write(new Uint8Array([1, 2, 3])); - await closeDeferred.promise; - conn.close(); - }); - const conn = await Deno.dial(addr); - conn.closeWrite(); // closing write - const buf = new Uint8Array(1024); - // Check read not impacted - const readResult = await conn.read(buf); - assertEquals(3, readResult); - assertEquals(1, buf[0]); - assertEquals(2, buf[1]); - assertEquals(3, buf[2]); - // Check write should be closed - let err; - try { - await conn.write(new Uint8Array([1, 2, 3])); - } catch (e) { - err = e; - } - assert(!!err); - assertEquals(err.kind, Deno.ErrorKind.BrokenPipe); - assertEquals(err.name, "BrokenPipe"); - closeDeferred.resolve(); - listener.close(); - conn.close(); -}); -*/ - -/* TODO Fix broken test. -testPerm({ net: true }, async function netDoubleCloseWrite() { - const addr = "127.0.0.1:4500"; - const listener = Deno.listen(addr); - const closeDeferred = deferred(); - listener.accept().then(async conn => { - await closeDeferred.promise; - conn.close(); - }); - const conn = await Deno.dial(addr); - conn.closeWrite(); // closing write - let err; - try { - // Duplicated close should throw error - conn.closeWrite(); - } catch (e) { - err = e; - } - assert(!!err); - assertEquals(err.kind, Deno.ErrorKind.NotConnected); - assertEquals(err.name, "NotConnected"); - closeDeferred.resolve(); - listener.close(); - conn.close(); -}); -*/ diff --git a/js/os.ts b/js/os.ts deleted file mode 100644 index 2fc06434a..000000000 --- a/js/os.ts +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { core } from "./core.ts"; -import * as dispatch from "./dispatch.ts"; -import { sendSync } from "./dispatch_json.ts"; -import { assert } from "./util.ts"; -import * as util from "./util.ts"; -import { window } from "./window.ts"; -import { OperatingSystem, Arch } from "./build.ts"; - -// builtin modules -import { _setGlobals } from "./deno.ts"; - -/** Check if running in terminal. - * - * console.log(Deno.isTTY().stdout); - */ -export function isTTY(): { stdin: boolean; stdout: boolean; stderr: boolean } { - return sendSync(dispatch.OP_IS_TTY); -} - -/** Get the hostname. - * Requires the `--allow-env` flag. - * - * console.log(Deno.hostname()); - */ -export function hostname(): string { - return sendSync(dispatch.OP_HOSTNAME); -} - -/** Exit the Deno process with optional exit code. */ -export function exit(code = 0): never { - sendSync(dispatch.OP_EXIT, { code }); - return util.unreachable(); -} - -function setEnv(key: string, value: string): void { - sendSync(dispatch.OP_SET_ENV, { key, value }); -} - -function getEnv(key: string): string | undefined { - return sendSync(dispatch.OP_GET_ENV, { key })[0]; -} - -/** Returns a snapshot of the environment variables at invocation. Mutating a - * property in the object will set that variable in the environment for - * the process. The environment object will only accept `string`s - * as values. - * - * console.log(Deno.env("SHELL")); - * const myEnv = Deno.env(); - * console.log(myEnv.SHELL); - * myEnv.TEST_VAR = "HELLO"; - * const newEnv = Deno.env(); - * console.log(myEnv.TEST_VAR == newEnv.TEST_VAR); - */ -export function env(): { [index: string]: string }; -export function env(key: string): string | undefined; -export function env( - key?: string -): { [index: string]: string } | string | undefined { - if (key) { - return getEnv(key); - } - const env = sendSync(dispatch.OP_ENV); - return new Proxy(env, { - set(obj, prop: string, value: string): boolean { - setEnv(prop, value); - return Reflect.set(obj, prop, value); - } - }); -} - -interface Start { - cwd: string; - pid: number; - argv: string[]; - mainModule: string; // Absolute URL. - debugFlag: boolean; - depsFlag: boolean; - typesFlag: boolean; - versionFlag: boolean; - denoVersion: string; - v8Version: string; - tsVersion: string; - noColor: boolean; - xevalDelim: string; - os: OperatingSystem; - arch: Arch; -} - -// This function bootstraps an environment within Deno, it is shared both by -// the runtime and the compiler environments. -// @internal -export function start(preserveDenoNamespace = true, source?: string): Start { - core.setAsyncHandler(dispatch.asyncMsgFromRust); - const ops = core.ops(); - // TODO(bartlomieju): this is a prototype, we should come up with - // something a bit more sophisticated - for (const [name, opId] of Object.entries(ops)) { - const opName = `OP_${name.toUpperCase()}`; - // Assign op ids to actual variables - dispatch[opName] = opId; - } - // First we send an empty `Start` message to let the privileged side know we - // are ready. The response should be a `StartRes` message containing the CLI - // args and other info. - const s = sendSync(dispatch.OP_START); - - util.setLogDebug(s.debugFlag, source); - - // pid and noColor need to be set in the Deno module before it's set to be - // frozen. - _setGlobals(s.pid, s.noColor); - delete window.Deno._setGlobals; - Object.freeze(window.Deno); - - if (preserveDenoNamespace) { - util.immutableDefine(window, "Deno", window.Deno); - // Deno.core could ONLY be safely frozen here (not in globals.ts) - // since shared_queue.js will modify core properties. - Object.freeze(window.Deno.core); - // core.sharedQueue is an object so we should also freeze it. - Object.freeze(window.Deno.core.sharedQueue); - } else { - // Remove window.Deno - delete window.Deno; - assert(window.Deno === undefined); - } - - return s; -} - -/** - * Returns the current user's home directory. - * Requires the `--allow-env` flag. - */ -export function homeDir(): string { - const path = sendSync(dispatch.OP_HOME_DIR); - if (!path) { - throw new Error("Could not get home directory."); - } - return path; -} - -/** - * Returns the path to the current deno executable. - * Requires the `--allow-env` flag. - */ -export function execPath(): string { - return sendSync(dispatch.OP_EXEC_PATH); -} diff --git a/js/os_test.ts b/js/os_test.ts deleted file mode 100644 index 0d07df1b4..000000000 --- a/js/os_test.ts +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { - test, - testPerm, - assert, - assertEquals, - assertNotEquals -} from "./test_util.ts"; - -testPerm({ env: true }, function envSuccess(): void { - const env = Deno.env(); - assert(env !== null); - // eslint-disable-next-line @typescript-eslint/camelcase - env.test_var = "Hello World"; - const newEnv = Deno.env(); - assertEquals(env.test_var, newEnv.test_var); - assertEquals(Deno.env("test_var"), env.test_var); -}); - -testPerm({ env: true }, function envNotFound(): void { - const r = Deno.env("env_var_does_not_exist!"); - assertEquals(r, undefined); -}); - -test(function envPermissionDenied1(): void { - let err; - try { - Deno.env(); - } catch (e) { - err = e; - } - assertNotEquals(err, undefined); - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -test(function envPermissionDenied2(): void { - let err; - try { - Deno.env("PATH"); - } catch (e) { - err = e; - } - assertNotEquals(err, undefined); - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -if (Deno.build.os === "win") { - // This test verifies that on Windows, environment variables are - // case-insensitive. Case normalization needs be done using the collation - // that Windows uses, rather than naively using String.toLowerCase(). - testPerm({ env: true, run: true }, async function envCaseInsensitive() { - // Utility function that runs a Deno subprocess with the environment - // specified in `inputEnv`. The subprocess reads the environment variables - // which are in the keys of `expectedEnv` and writes them to stdout as JSON. - // It is then verified that these match with the values of `expectedEnv`. - const checkChildEnv = async (inputEnv, expectedEnv): Promise<void> => { - const src = ` - console.log( - ${JSON.stringify(Object.keys(expectedEnv))}.map(k => Deno.env(k)) - )`; - const proc = Deno.run({ - args: [Deno.execPath(), "eval", src], - env: inputEnv, - stdout: "piped" - }); - const status = await proc.status(); - assertEquals(status.success, true); - const expectedValues = Object.values(expectedEnv); - const actualValues = JSON.parse( - new TextDecoder().decode(await proc.output()) - ); - assertEquals(actualValues, expectedValues); - }; - - assertEquals(Deno.env("path"), Deno.env("PATH")); - assertEquals(Deno.env("Path"), Deno.env("PATH")); - - // Check 'foo', 'Foo' and 'Foo' are case folded. - await checkChildEnv({ foo: "X" }, { foo: "X", Foo: "X", FOO: "X" }); - - // Check that 'µ' and 'Μ' are not case folded. - const lc1 = "µ"; - const uc1 = lc1.toUpperCase(); - assertNotEquals(lc1, uc1); - await checkChildEnv( - { [lc1]: "mu", [uc1]: "MU" }, - { [lc1]: "mu", [uc1]: "MU" } - ); - - // Check that 'dž' and 'DŽ' are folded, but 'Dž' is preserved. - const c2 = "Dž"; - const lc2 = c2.toLowerCase(); - const uc2 = c2.toUpperCase(); - assertNotEquals(c2, lc2); - assertNotEquals(c2, uc2); - await checkChildEnv( - { [c2]: "Dz", [lc2]: "dz" }, - { [c2]: "Dz", [lc2]: "dz", [uc2]: "dz" } - ); - await checkChildEnv( - { [c2]: "Dz", [uc2]: "DZ" }, - { [c2]: "Dz", [uc2]: "DZ", [lc2]: "DZ" } - ); - }); -} - -test(function osPid(): void { - console.log("pid", Deno.pid); - assert(Deno.pid > 0); -}); - -// See complete tests in tools/is_tty_test.py -test(function osIsTTYSmoke(): void { - console.log(Deno.isTTY()); -}); - -testPerm({ env: true }, function homeDir(): void { - assertNotEquals(Deno.homeDir(), ""); -}); - -testPerm({ env: false }, function homeDirPerm(): void { - let caughtError = false; - try { - Deno.homeDir(); - } catch (err) { - caughtError = true; - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); - } - assert(caughtError); -}); - -testPerm({ env: true }, function execPath(): void { - assertNotEquals(Deno.execPath(), ""); -}); - -testPerm({ env: false }, function execPathPerm(): void { - let caughtError = false; - try { - Deno.execPath(); - } catch (err) { - caughtError = true; - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); - } - assert(caughtError); -}); - -testPerm({ env: true }, function hostnameDir(): void { - assertNotEquals(Deno.hostname(), ""); -}); - -testPerm({ env: false }, function hostnamePerm(): void { - let caughtError = false; - try { - Deno.hostname(); - } catch (err) { - caughtError = true; - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); - } - assert(caughtError); -}); diff --git a/js/performance.ts b/js/performance.ts deleted file mode 100644 index 6ea8e56e1..000000000 --- a/js/performance.ts +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as dispatch from "./dispatch.ts"; -import { sendSync } from "./dispatch_json.ts"; - -interface NowResponse { - seconds: number; - subsecNanos: number; -} - -export class Performance { - /** Returns a current time from Deno's start in milliseconds. - * - * Use the flag --allow-hrtime return a precise value. - * - * const t = performance.now(); - * console.log(`${t} ms since start!`); - */ - now(): number { - const res = sendSync(dispatch.OP_NOW) as NowResponse; - return res.seconds * 1e3 + res.subsecNanos / 1e6; - } -} diff --git a/js/performance_test.ts b/js/performance_test.ts deleted file mode 100644 index ac682364e..000000000 --- a/js/performance_test.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { testPerm, assert } from "./test_util.ts"; - -testPerm({ hrtime: false }, function now(): void { - const start = performance.now(); - setTimeout((): void => { - const end = performance.now(); - assert(end - start >= 10); - }, 10); -}); diff --git a/js/permissions.ts b/js/permissions.ts deleted file mode 100644 index 4f393501c..000000000 --- a/js/permissions.ts +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as dispatch from "./dispatch.ts"; -import { sendSync } from "./dispatch_json.ts"; - -/** Permissions as granted by the caller */ -export interface Permissions { - read: boolean; - write: boolean; - net: boolean; - env: boolean; - run: boolean; - hrtime: boolean; - // NOTE: Keep in sync with src/permissions.rs -} - -export type Permission = keyof Permissions; - -/** Inspect granted permissions for the current program. - * - * if (Deno.permissions().read) { - * const file = await Deno.readFile("example.test"); - * // ... - * } - */ -export function permissions(): Permissions { - return sendSync(dispatch.OP_PERMISSIONS) as Permissions; -} - -/** Revoke a permission. When the permission was already revoked nothing changes - * - * if (Deno.permissions().read) { - * const file = await Deno.readFile("example.test"); - * Deno.revokePermission('read'); - * } - * Deno.readFile("example.test"); // -> error or permission prompt - */ -export function revokePermission(permission: Permission): void { - sendSync(dispatch.OP_REVOKE_PERMISSION, { permission }); -} diff --git a/js/permissions_test.ts b/js/permissions_test.ts deleted file mode 100644 index 6511c2dcb..000000000 --- a/js/permissions_test.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { testPerm, assert, assertEquals } from "./test_util.ts"; - -const knownPermissions: Deno.Permission[] = [ - "run", - "read", - "write", - "net", - "env", - "hrtime" -]; - -for (const grant of knownPermissions) { - testPerm({ [grant]: true }, function envGranted(): void { - const perms = Deno.permissions(); - assert(perms !== null); - for (const perm in perms) { - assertEquals(perms[perm], perm === grant); - } - - Deno.revokePermission(grant); - - const revoked = Deno.permissions(); - for (const perm in revoked) { - assertEquals(revoked[perm], false); - } - }); -} diff --git a/js/process.ts b/js/process.ts deleted file mode 100644 index 0c77929f9..000000000 --- a/js/process.ts +++ /dev/null @@ -1,307 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendSync, sendAsync } from "./dispatch_json.ts"; -import * as dispatch from "./dispatch.ts"; -import { File, close } from "./files.ts"; -import { ReadCloser, WriteCloser } from "./io.ts"; -import { readAll } from "./buffer.ts"; -import { assert, unreachable } from "./util.ts"; -import { build } from "./build.ts"; - -/** How to handle subprocess stdio. - * - * "inherit" The default if unspecified. The child inherits from the - * corresponding parent descriptor. - * - * "piped" A new pipe should be arranged to connect the parent and child - * subprocesses. - * - * "null" This stream will be ignored. This is the equivalent of attaching the - * stream to /dev/null. - */ -export type ProcessStdio = "inherit" | "piped" | "null"; - -// TODO Maybe extend VSCode's 'CommandOptions'? -// See https://code.visualstudio.com/docs/editor/tasks-appendix#_schema-for-tasksjson -export interface RunOptions { - args: string[]; - cwd?: string; - env?: { [key: string]: string }; - stdout?: ProcessStdio | number; - stderr?: ProcessStdio | number; - stdin?: ProcessStdio | number; -} - -interface RunStatusResponse { - gotSignal: boolean; - exitCode: number; - exitSignal: number; -} - -async function runStatus(rid: number): Promise<ProcessStatus> { - const res = (await sendAsync(dispatch.OP_RUN_STATUS, { - rid - })) as RunStatusResponse; - - if (res.gotSignal) { - const signal = res.exitSignal; - return { signal, success: false }; - } else { - const code = res.exitCode; - return { code, success: code === 0 }; - } -} - -/** Send a signal to process under given PID. Unix only at this moment. - * If pid is negative, the signal will be sent to the process group identified - * by -pid. - * Requires the `--allow-run` flag. - */ -export function kill(pid: number, signo: number): void { - sendSync(dispatch.OP_KILL, { pid, signo }); -} - -export class Process { - readonly rid: number; - readonly pid: number; - readonly stdin?: WriteCloser; - readonly stdout?: ReadCloser; - readonly stderr?: ReadCloser; - - // @internal - constructor(res: RunResponse) { - this.rid = res.rid; - this.pid = res.pid; - - if (res.stdinRid && res.stdinRid > 0) { - this.stdin = new File(res.stdinRid); - } - - if (res.stdoutRid && res.stdoutRid > 0) { - this.stdout = new File(res.stdoutRid); - } - - if (res.stderrRid && res.stderrRid > 0) { - this.stderr = new File(res.stderrRid); - } - } - - async status(): Promise<ProcessStatus> { - return await runStatus(this.rid); - } - - /** Buffer the stdout and return it as Uint8Array after EOF. - * You must set stdout to "piped" when creating the process. - * This calls close() on stdout after its done. - */ - async output(): Promise<Uint8Array> { - if (!this.stdout) { - throw new Error("Process.output: stdout is undefined"); - } - try { - return await readAll(this.stdout); - } finally { - this.stdout.close(); - } - } - - /** Buffer the stderr and return it as Uint8Array after EOF. - * You must set stderr to "piped" when creating the process. - * This calls close() on stderr after its done. - */ - async stderrOutput(): Promise<Uint8Array> { - if (!this.stderr) { - throw new Error("Process.stderrOutput: stderr is undefined"); - } - try { - return await readAll(this.stderr); - } finally { - this.stderr.close(); - } - } - - close(): void { - close(this.rid); - } - - kill(signo: number): void { - kill(this.pid, signo); - } -} - -export interface ProcessStatus { - success: boolean; - code?: number; - signal?: number; // TODO: Make this a string, e.g. 'SIGTERM'. -} - -// TODO: this method is only used to validate proper option, probably can be renamed -function stdioMap(s: string): string { - switch (s) { - case "inherit": - case "piped": - case "null": - return s; - default: - return unreachable(); - } -} - -function isRid(arg: unknown): arg is number { - return !isNaN(arg as number); -} - -interface RunResponse { - rid: number; - pid: number; - stdinRid: number | null; - stdoutRid: number | null; - stderrRid: number | null; -} -/** - * Spawns new subprocess. - * - * Subprocess uses same working directory as parent process unless `opt.cwd` - * is specified. - * - * Environmental variables for subprocess can be specified using `opt.env` - * mapping. - * - * By default subprocess inherits stdio of parent process. To change that - * `opt.stdout`, `opt.stderr` and `opt.stdin` can be specified independently - - * they can be set to either `ProcessStdio` or `rid` of open file. - */ -export function run(opt: RunOptions): Process { - assert(opt.args.length > 0); - let env: Array<[string, string]> = []; - if (opt.env) { - env = Array.from(Object.entries(opt.env)); - } - - let stdin = stdioMap("inherit"); - let stdout = stdioMap("inherit"); - let stderr = stdioMap("inherit"); - let stdinRid = 0; - let stdoutRid = 0; - let stderrRid = 0; - - if (opt.stdin) { - if (isRid(opt.stdin)) { - stdinRid = opt.stdin; - } else { - stdin = stdioMap(opt.stdin); - } - } - - if (opt.stdout) { - if (isRid(opt.stdout)) { - stdoutRid = opt.stdout; - } else { - stdout = stdioMap(opt.stdout); - } - } - - if (opt.stderr) { - if (isRid(opt.stderr)) { - stderrRid = opt.stderr; - } else { - stderr = stdioMap(opt.stderr); - } - } - - const req = { - args: opt.args.map(String), - cwd: opt.cwd, - env, - stdin, - stdout, - stderr, - stdinRid, - stdoutRid, - stderrRid - }; - - const res = sendSync(dispatch.OP_RUN, req) as RunResponse; - return new Process(res); -} - -// From `kill -l` -enum LinuxSignal { - SIGHUP = 1, - SIGINT = 2, - SIGQUIT = 3, - SIGILL = 4, - SIGTRAP = 5, - SIGABRT = 6, - SIGBUS = 7, - SIGFPE = 8, - SIGKILL = 9, - SIGUSR1 = 10, - SIGSEGV = 11, - SIGUSR2 = 12, - SIGPIPE = 13, - SIGALRM = 14, - SIGTERM = 15, - SIGSTKFLT = 16, - SIGCHLD = 17, - SIGCONT = 18, - SIGSTOP = 19, - SIGTSTP = 20, - SIGTTIN = 21, - SIGTTOU = 22, - SIGURG = 23, - SIGXCPU = 24, - SIGXFSZ = 25, - SIGVTALRM = 26, - SIGPROF = 27, - SIGWINCH = 28, - SIGIO = 29, - SIGPWR = 30, - SIGSYS = 31 -} - -// From `kill -l` -enum MacOSSignal { - SIGHUP = 1, - SIGINT = 2, - SIGQUIT = 3, - SIGILL = 4, - SIGTRAP = 5, - SIGABRT = 6, - SIGEMT = 7, - SIGFPE = 8, - SIGKILL = 9, - SIGBUS = 10, - SIGSEGV = 11, - SIGSYS = 12, - SIGPIPE = 13, - SIGALRM = 14, - SIGTERM = 15, - SIGURG = 16, - SIGSTOP = 17, - SIGTSTP = 18, - SIGCONT = 19, - SIGCHLD = 20, - SIGTTIN = 21, - SIGTTOU = 22, - SIGIO = 23, - SIGXCPU = 24, - SIGXFSZ = 25, - SIGVTALRM = 26, - SIGPROF = 27, - SIGWINCH = 28, - SIGINFO = 29, - SIGUSR1 = 30, - SIGUSR2 = 31 -} - -/** Signals numbers. This is platform dependent. - */ -export const Signal = {}; - -export function setSignals(): void { - if (build.os === "mac") { - Object.assign(Signal, MacOSSignal); - } else { - Object.assign(Signal, LinuxSignal); - } -} diff --git a/js/process_test.ts b/js/process_test.ts deleted file mode 100644 index 42db06dee..000000000 --- a/js/process_test.ts +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { - test, - testPerm, - assert, - assertEquals, - assertStrContains -} from "./test_util.ts"; -const { - kill, - run, - DenoError, - ErrorKind, - readFile, - open, - makeTempDir, - writeFile -} = Deno; - -test(function runPermissions(): void { - let caughtError = false; - try { - Deno.run({ args: ["python", "-c", "print('hello world')"] }); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); - -testPerm({ run: true }, async function runSuccess(): Promise<void> { - const p = run({ - args: ["python", "-c", "print('hello world')"] - }); - const status = await p.status(); - console.log("status", status); - assertEquals(status.success, true); - assertEquals(status.code, 0); - assertEquals(status.signal, undefined); - p.close(); -}); - -testPerm({ run: true }, async function runCommandFailedWithCode(): Promise< - void -> { - const p = run({ - args: ["python", "-c", "import sys;sys.exit(41 + 1)"] - }); - const status = await p.status(); - assertEquals(status.success, false); - assertEquals(status.code, 42); - assertEquals(status.signal, undefined); - p.close(); -}); - -testPerm({ run: true }, async function runCommandFailedWithSignal(): Promise< - void -> { - if (Deno.build.os === "win") { - return; // No signals on windows. - } - const p = run({ - args: ["python", "-c", "import os;os.kill(os.getpid(), 9)"] - }); - const status = await p.status(); - assertEquals(status.success, false); - assertEquals(status.code, undefined); - assertEquals(status.signal, 9); - p.close(); -}); - -testPerm({ run: true }, function runNotFound(): void { - let error; - try { - run({ args: ["this file hopefully doesn't exist"] }); - } catch (e) { - error = e; - } - assert(error !== undefined); - assert(error instanceof DenoError); - assertEquals(error.kind, ErrorKind.NotFound); -}); - -testPerm( - { write: true, run: true }, - async function runWithCwdIsAsync(): Promise<void> { - const enc = new TextEncoder(); - const cwd = await makeTempDir({ prefix: "deno_command_test" }); - - const exitCodeFile = "deno_was_here"; - const pyProgramFile = "poll_exit.py"; - const pyProgram = ` -from sys import exit -from time import sleep - -while True: - try: - with open("${exitCodeFile}", "r") as f: - line = f.readline() - code = int(line) - exit(code) - except IOError: - # Retry if we got here before deno wrote the file. - sleep(0.01) - pass -`; - - Deno.writeFileSync(`${cwd}/${pyProgramFile}.py`, enc.encode(pyProgram)); - const p = run({ - cwd, - args: ["python", `${pyProgramFile}.py`] - }); - - // Write the expected exit code *after* starting python. - // This is how we verify that `run()` is actually asynchronous. - const code = 84; - Deno.writeFileSync(`${cwd}/${exitCodeFile}`, enc.encode(`${code}`)); - - const status = await p.status(); - assertEquals(status.success, false); - assertEquals(status.code, code); - assertEquals(status.signal, undefined); - p.close(); - } -); - -testPerm({ run: true }, async function runStdinPiped(): Promise<void> { - const p = run({ - args: ["python", "-c", "import sys; assert 'hello' == sys.stdin.read();"], - stdin: "piped" - }); - assert(!p.stdout); - assert(!p.stderr); - - const msg = new TextEncoder().encode("hello"); - const n = await p.stdin.write(msg); - assertEquals(n, msg.byteLength); - - p.stdin.close(); - - const status = await p.status(); - assertEquals(status.success, true); - assertEquals(status.code, 0); - assertEquals(status.signal, undefined); - p.close(); -}); - -testPerm({ run: true }, async function runStdoutPiped(): Promise<void> { - const p = run({ - args: ["python", "-c", "import sys; sys.stdout.write('hello')"], - stdout: "piped" - }); - assert(!p.stdin); - assert(!p.stderr); - - const data = new Uint8Array(10); - let r = await p.stdout.read(data); - if (r === Deno.EOF) { - throw new Error("p.stdout.read(...) should not be EOF"); - } - assertEquals(r, 5); - const s = new TextDecoder().decode(data.subarray(0, r)); - assertEquals(s, "hello"); - r = await p.stdout.read(data); - assertEquals(r, Deno.EOF); - p.stdout.close(); - - const status = await p.status(); - assertEquals(status.success, true); - assertEquals(status.code, 0); - assertEquals(status.signal, undefined); - p.close(); -}); - -testPerm({ run: true }, async function runStderrPiped(): Promise<void> { - const p = run({ - args: ["python", "-c", "import sys; sys.stderr.write('hello')"], - stderr: "piped" - }); - assert(!p.stdin); - assert(!p.stdout); - - const data = new Uint8Array(10); - let r = await p.stderr.read(data); - if (r === Deno.EOF) { - throw new Error("p.stderr.read should not return EOF here"); - } - assertEquals(r, 5); - const s = new TextDecoder().decode(data.subarray(0, r)); - assertEquals(s, "hello"); - r = await p.stderr.read(data); - assertEquals(r, Deno.EOF); - p.stderr.close(); - - const status = await p.status(); - assertEquals(status.success, true); - assertEquals(status.code, 0); - assertEquals(status.signal, undefined); - p.close(); -}); - -testPerm({ run: true }, async function runOutput(): Promise<void> { - const p = run({ - args: ["python", "-c", "import sys; sys.stdout.write('hello')"], - stdout: "piped" - }); - const output = await p.output(); - const s = new TextDecoder().decode(output); - assertEquals(s, "hello"); - p.close(); -}); - -testPerm({ run: true }, async function runStderrOutput(): Promise<void> { - const p = run({ - args: ["python", "-c", "import sys; sys.stderr.write('error')"], - stderr: "piped" - }); - const error = await p.stderrOutput(); - const s = new TextDecoder().decode(error); - assertEquals(s, "error"); - p.close(); -}); - -testPerm( - { run: true, write: true, read: true }, - async function runRedirectStdoutStderr(): Promise<void> { - const tempDir = await makeTempDir(); - const fileName = tempDir + "/redirected_stdio.txt"; - const file = await open(fileName, "w"); - - const p = run({ - args: [ - "python", - "-c", - "import sys; sys.stderr.write('error\\n'); sys.stdout.write('output\\n');" - ], - stdout: file.rid, - stderr: file.rid - }); - - await p.status(); - p.close(); - file.close(); - - const fileContents = await readFile(fileName); - const decoder = new TextDecoder(); - const text = decoder.decode(fileContents); - - assertStrContains(text, "error"); - assertStrContains(text, "output"); - } -); - -testPerm( - { run: true, write: true, read: true }, - async function runRedirectStdin(): Promise<void> { - const tempDir = await makeTempDir(); - const fileName = tempDir + "/redirected_stdio.txt"; - const encoder = new TextEncoder(); - await writeFile(fileName, encoder.encode("hello")); - const file = await open(fileName, "r"); - - const p = run({ - args: ["python", "-c", "import sys; assert 'hello' == sys.stdin.read();"], - stdin: file.rid - }); - - const status = await p.status(); - assertEquals(status.code, 0); - p.close(); - file.close(); - } -); - -testPerm({ run: true }, async function runEnv(): Promise<void> { - const p = run({ - args: [ - "python", - "-c", - "import os, sys; sys.stdout.write(os.environ.get('FOO', '') + os.environ.get('BAR', ''))" - ], - env: { - FOO: "0123", - BAR: "4567" - }, - stdout: "piped" - }); - const output = await p.output(); - const s = new TextDecoder().decode(output); - assertEquals(s, "01234567"); - p.close(); -}); - -testPerm({ run: true }, async function runClose(): Promise<void> { - const p = run({ - args: [ - "python", - "-c", - "from time import sleep; import sys; sleep(10000); sys.stderr.write('error')" - ], - stderr: "piped" - }); - assert(!p.stdin); - assert(!p.stdout); - - p.close(); - - const data = new Uint8Array(10); - const r = await p.stderr.read(data); - assertEquals(r, Deno.EOF); -}); - -test(function signalNumbers(): void { - if (Deno.build.os === "mac") { - assertEquals(Deno.Signal.SIGSTOP, 17); - } else if (Deno.build.os === "linux") { - assertEquals(Deno.Signal.SIGSTOP, 19); - } -}); - -// Ignore signal tests on windows for now... -if (Deno.build.os !== "win") { - test(function killPermissions(): void { - let caughtError = false; - try { - // Unlike the other test cases, we don't have permission to spawn a - // subprocess we can safely kill. Instead we send SIGCONT to the current - // process - assuming that Deno does not have a special handler set for it - // and will just continue even if a signal is erroneously sent. - Deno.kill(Deno.pid, Deno.Signal.SIGCONT); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); - }); - - testPerm({ run: true }, async function killSuccess(): Promise<void> { - const p = run({ - args: ["python", "-c", "from time import sleep; sleep(10000)"] - }); - - assertEquals(Deno.Signal.SIGINT, 2); - kill(p.pid, Deno.Signal.SIGINT); - const status = await p.status(); - - assertEquals(status.success, false); - // TODO(ry) On Linux, status.code is sometimes undefined and sometimes 1. - // The following assert is causing this test to be flaky. Investigate and - // re-enable when it can be made deterministic. - // assertEquals(status.code, 1); - // assertEquals(status.signal, Deno.Signal.SIGINT); - }); - - testPerm({ run: true }, async function killFailed(): Promise<void> { - const p = run({ - args: ["python", "-c", "from time import sleep; sleep(10000)"] - }); - assert(!p.stdin); - assert(!p.stdout); - - let err; - try { - kill(p.pid, 12345); - } catch (e) { - err = e; - } - - assert(!!err); - assertEquals(err.kind, Deno.ErrorKind.InvalidInput); - assertEquals(err.name, "InvalidInput"); - - p.close(); - }); -} diff --git a/js/read_dir.ts b/js/read_dir.ts deleted file mode 100644 index 2fa6a566b..000000000 --- a/js/read_dir.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendSync, sendAsync } from "./dispatch_json.ts"; -import * as dispatch from "./dispatch.ts"; -import { FileInfo, FileInfoImpl } from "./file_info.ts"; -import { StatResponse } from "./stat.ts"; - -interface ReadDirResponse { - entries: StatResponse[]; -} - -function res(response: ReadDirResponse): FileInfo[] { - return response.entries.map( - (statRes: StatResponse): FileInfo => { - return new FileInfoImpl(statRes); - } - ); -} - -/** Reads the directory given by path and returns a list of file info - * synchronously. - * - * const files = Deno.readDirSync("/"); - */ -export function readDirSync(path: string): FileInfo[] { - return res(sendSync(dispatch.OP_READ_DIR, { path })); -} - -/** Reads the directory given by path and returns a list of file info. - * - * const files = await Deno.readDir("/"); - */ -export async function readDir(path: string): Promise<FileInfo[]> { - return res(await sendAsync(dispatch.OP_READ_DIR, { path })); -} diff --git a/js/read_dir_test.ts b/js/read_dir_test.ts deleted file mode 100644 index 3e11df9fe..000000000 --- a/js/read_dir_test.ts +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { testPerm, assert, assertEquals } from "./test_util.ts"; - -type FileInfo = Deno.FileInfo; - -function assertSameContent(files: FileInfo[]): void { - let counter = 0; - - for (const file of files) { - if (file.name === "subdir") { - assert(file.isDirectory()); - counter++; - } - - if (file.name === "002_hello.ts") { - assertEquals(file.mode!, Deno.statSync(`tests/${file.name}`).mode!); - counter++; - } - } - - assertEquals(counter, 2); -} - -testPerm({ read: true }, function readDirSyncSuccess(): void { - const files = Deno.readDirSync("tests/"); - assertSameContent(files); -}); - -testPerm({ read: false }, function readDirSyncPerm(): void { - let caughtError = false; - try { - Deno.readDirSync("tests/"); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); - -testPerm({ read: true }, function readDirSyncNotDir(): void { - let caughtError = false; - let src; - - try { - src = Deno.readDirSync("package.json"); - } catch (err) { - caughtError = true; - assertEquals(err.kind, Deno.ErrorKind.Other); - } - assert(caughtError); - assertEquals(src, undefined); -}); - -testPerm({ read: true }, function readDirSyncNotFound(): void { - let caughtError = false; - let src; - - try { - src = Deno.readDirSync("bad_dir_name"); - } catch (err) { - caughtError = true; - assertEquals(err.kind, Deno.ErrorKind.NotFound); - } - assert(caughtError); - assertEquals(src, undefined); -}); - -testPerm({ read: true }, async function readDirSuccess(): Promise<void> { - const files = await Deno.readDir("tests/"); - assertSameContent(files); -}); - -testPerm({ read: false }, async function readDirPerm(): Promise<void> { - let caughtError = false; - try { - await Deno.readDir("tests/"); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); diff --git a/js/read_file.ts b/js/read_file.ts deleted file mode 100644 index de6630cc0..000000000 --- a/js/read_file.ts +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { open, openSync } from "./files.ts"; -import { readAll, readAllSync } from "./buffer.ts"; - -/** Read the entire contents of a file synchronously. - * - * const decoder = new TextDecoder("utf-8"); - * const data = Deno.readFileSync("hello.txt"); - * console.log(decoder.decode(data)); - */ -export function readFileSync(filename: string): Uint8Array { - const file = openSync(filename); - const contents = readAllSync(file); - file.close(); - return contents; -} - -/** Read the entire contents of a file. - * - * const decoder = new TextDecoder("utf-8"); - * const data = await Deno.readFile("hello.txt"); - * console.log(decoder.decode(data)); - */ -export async function readFile(filename: string): Promise<Uint8Array> { - const file = await open(filename); - const contents = await readAll(file); - file.close(); - return contents; -} diff --git a/js/read_file_test.ts b/js/read_file_test.ts deleted file mode 100644 index 7d4f4789c..000000000 --- a/js/read_file_test.ts +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { testPerm, assert, assertEquals } from "./test_util.ts"; - -testPerm({ read: true }, function readFileSyncSuccess(): void { - const data = Deno.readFileSync("package.json"); - assert(data.byteLength > 0); - const decoder = new TextDecoder("utf-8"); - const json = decoder.decode(data); - const pkg = JSON.parse(json); - assertEquals(pkg.name, "deno"); -}); - -testPerm({ read: false }, function readFileSyncPerm(): void { - let caughtError = false; - try { - Deno.readFileSync("package.json"); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); - -testPerm({ read: true }, function readFileSyncNotFound(): void { - let caughtError = false; - let data; - try { - data = Deno.readFileSync("bad_filename"); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.NotFound); - } - assert(caughtError); - assert(data === undefined); -}); - -testPerm({ read: true }, async function readFileSuccess(): Promise<void> { - const data = await Deno.readFile("package.json"); - assert(data.byteLength > 0); - const decoder = new TextDecoder("utf-8"); - const json = decoder.decode(data); - const pkg = JSON.parse(json); - assertEquals(pkg.name, "deno"); -}); - -testPerm({ read: false }, async function readFilePerm(): Promise<void> { - let caughtError = false; - try { - await Deno.readFile("package.json"); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); diff --git a/js/read_link.ts b/js/read_link.ts deleted file mode 100644 index 861fbff0b..000000000 --- a/js/read_link.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendSync, sendAsync } from "./dispatch_json.ts"; -import * as dispatch from "./dispatch.ts"; - -/** Returns the destination of the named symbolic link synchronously. - * - * const targetPath = Deno.readlinkSync("symlink/path"); - */ -export function readlinkSync(name: string): string { - return sendSync(dispatch.OP_READ_LINK, { name }); -} - -/** Returns the destination of the named symbolic link. - * - * const targetPath = await Deno.readlink("symlink/path"); - */ -export async function readlink(name: string): Promise<string> { - return await sendAsync(dispatch.OP_READ_LINK, { name }); -} diff --git a/js/read_link_test.ts b/js/read_link_test.ts deleted file mode 100644 index 83a693e3b..000000000 --- a/js/read_link_test.ts +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { testPerm, assert, assertEquals } from "./test_util.ts"; - -testPerm({ write: true, read: true }, function readlinkSyncSuccess(): void { - const testDir = Deno.makeTempDirSync(); - const target = testDir + "/target"; - const symlink = testDir + "/symln"; - Deno.mkdirSync(target); - // TODO Add test for Windows once symlink is implemented for Windows. - // See https://github.com/denoland/deno/issues/815. - if (Deno.build.os !== "win") { - Deno.symlinkSync(target, symlink); - const targetPath = Deno.readlinkSync(symlink); - assertEquals(targetPath, target); - } -}); - -testPerm({ read: false }, async function readlinkSyncPerm(): Promise<void> { - let caughtError = false; - try { - Deno.readlinkSync("/symlink"); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); - -testPerm({ read: true }, function readlinkSyncNotFound(): void { - let caughtError = false; - let data; - try { - data = Deno.readlinkSync("bad_filename"); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.NotFound); - } - assert(caughtError); - assertEquals(data, undefined); -}); - -testPerm({ write: true, read: true }, async function readlinkSuccess(): Promise< - void -> { - const testDir = Deno.makeTempDirSync(); - const target = testDir + "/target"; - const symlink = testDir + "/symln"; - Deno.mkdirSync(target); - // TODO Add test for Windows once symlink is implemented for Windows. - // See https://github.com/denoland/deno/issues/815. - if (Deno.build.os !== "win") { - Deno.symlinkSync(target, symlink); - const targetPath = await Deno.readlink(symlink); - assertEquals(targetPath, target); - } -}); - -testPerm({ read: false }, async function readlinkPerm(): Promise<void> { - let caughtError = false; - try { - await Deno.readlink("/symlink"); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); diff --git a/js/remove.ts b/js/remove.ts deleted file mode 100644 index 36413a7c4..000000000 --- a/js/remove.ts +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendSync, sendAsync } from "./dispatch_json.ts"; -import * as dispatch from "./dispatch.ts"; - -export interface RemoveOption { - recursive?: boolean; -} - -/** Removes the named file or directory synchronously. Would throw - * error if permission denied, not found, or directory not empty if `recursive` - * set to false. - * `recursive` is set to false by default. - * - * Deno.removeSync("/path/to/dir/or/file", {recursive: false}); - */ -export function removeSync(path: string, options: RemoveOption = {}): void { - sendSync(dispatch.OP_REMOVE, { path, recursive: !!options.recursive }); -} - -/** Removes the named file or directory. Would throw error if - * permission denied, not found, or directory not empty if `recursive` set - * to false. - * `recursive` is set to false by default. - * - * await Deno.remove("/path/to/dir/or/file", {recursive: false}); - */ -export async function remove( - path: string, - options: RemoveOption = {} -): Promise<void> { - await sendAsync(dispatch.OP_REMOVE, { path, recursive: !!options.recursive }); -} diff --git a/js/remove_test.ts b/js/remove_test.ts deleted file mode 100644 index f14386f7f..000000000 --- a/js/remove_test.ts +++ /dev/null @@ -1,335 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { testPerm, assert, assertEquals } from "./test_util.ts"; - -// SYNC - -testPerm({ write: true }, function removeSyncDirSuccess(): void { - // REMOVE EMPTY DIRECTORY - const path = Deno.makeTempDirSync() + "/dir/subdir"; - Deno.mkdirSync(path); - const pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory()); // check exist first - Deno.removeSync(path); // remove - // We then check again after remove - let err; - try { - Deno.statSync(path); - } catch (e) { - err = e; - } - // Directory is gone - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); - -testPerm({ write: true }, function removeSyncFileSuccess(): void { - // REMOVE FILE - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - Deno.writeFileSync(filename, data, { perm: 0o666 }); - const fileInfo = Deno.statSync(filename); - assert(fileInfo.isFile()); // check exist first - Deno.removeSync(filename); // remove - // We then check again after remove - let err; - try { - Deno.statSync(filename); - } catch (e) { - err = e; - } - // File is gone - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); - -testPerm({ write: true }, function removeSyncFail(): void { - // NON-EMPTY DIRECTORY - const path = Deno.makeTempDirSync() + "/dir/subdir"; - const subPath = path + "/subsubdir"; - Deno.mkdirSync(path); - Deno.mkdirSync(subPath); - const pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory()); // check exist first - const subPathInfo = Deno.statSync(subPath); - assert(subPathInfo.isDirectory()); // check exist first - let err; - try { - // Should not be able to recursively remove - Deno.removeSync(path); - } catch (e) { - err = e; - } - // TODO(ry) Is Other really the error we should get here? What would Go do? - assertEquals(err.kind, Deno.ErrorKind.Other); - assertEquals(err.name, "Other"); - // NON-EXISTENT DIRECTORY/FILE - try { - // Non-existent - Deno.removeSync("/baddir"); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); - -testPerm({ write: false }, function removeSyncPerm(): void { - let err; - try { - Deno.removeSync("/baddir"); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -testPerm({ write: true }, function removeAllSyncDirSuccess(): void { - // REMOVE EMPTY DIRECTORY - let path = Deno.makeTempDirSync() + "/dir/subdir"; - Deno.mkdirSync(path); - let pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory()); // check exist first - Deno.removeSync(path, { recursive: true }); // remove - // We then check again after remove - let err; - try { - Deno.statSync(path); - } catch (e) { - err = e; - } - // Directory is gone - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); - // REMOVE NON-EMPTY DIRECTORY - path = Deno.makeTempDirSync() + "/dir/subdir"; - const subPath = path + "/subsubdir"; - Deno.mkdirSync(path); - Deno.mkdirSync(subPath); - pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory()); // check exist first - const subPathInfo = Deno.statSync(subPath); - assert(subPathInfo.isDirectory()); // check exist first - Deno.removeSync(path, { recursive: true }); // remove - // We then check parent directory again after remove - try { - Deno.statSync(path); - } catch (e) { - err = e; - } - // Directory is gone - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); - -testPerm({ write: true }, function removeAllSyncFileSuccess(): void { - // REMOVE FILE - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - Deno.writeFileSync(filename, data, { perm: 0o666 }); - const fileInfo = Deno.statSync(filename); - assert(fileInfo.isFile()); // check exist first - Deno.removeSync(filename, { recursive: true }); // remove - // We then check again after remove - let err; - try { - Deno.statSync(filename); - } catch (e) { - err = e; - } - // File is gone - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); - -testPerm({ write: true }, function removeAllSyncFail(): void { - // NON-EXISTENT DIRECTORY/FILE - let err; - try { - // Non-existent - Deno.removeSync("/baddir", { recursive: true }); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); - -testPerm({ write: false }, function removeAllSyncPerm(): void { - let err; - try { - Deno.removeSync("/baddir", { recursive: true }); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -// ASYNC - -testPerm({ write: true }, async function removeDirSuccess(): Promise<void> { - // REMOVE EMPTY DIRECTORY - const path = Deno.makeTempDirSync() + "/dir/subdir"; - Deno.mkdirSync(path); - const pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory()); // check exist first - await Deno.remove(path); // remove - // We then check again after remove - let err; - try { - Deno.statSync(path); - } catch (e) { - err = e; - } - // Directory is gone - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); - -testPerm({ write: true }, async function removeFileSuccess(): Promise<void> { - // REMOVE FILE - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - Deno.writeFileSync(filename, data, { perm: 0o666 }); - const fileInfo = Deno.statSync(filename); - assert(fileInfo.isFile()); // check exist first - await Deno.remove(filename); // remove - // We then check again after remove - let err; - try { - Deno.statSync(filename); - } catch (e) { - err = e; - } - // File is gone - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); - -testPerm({ write: true }, async function removeFail(): Promise<void> { - // NON-EMPTY DIRECTORY - const path = Deno.makeTempDirSync() + "/dir/subdir"; - const subPath = path + "/subsubdir"; - Deno.mkdirSync(path); - Deno.mkdirSync(subPath); - const pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory()); // check exist first - const subPathInfo = Deno.statSync(subPath); - assert(subPathInfo.isDirectory()); // check exist first - let err; - try { - // Should not be able to recursively remove - await Deno.remove(path); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.Other); - assertEquals(err.name, "Other"); - // NON-EXISTENT DIRECTORY/FILE - try { - // Non-existent - await Deno.remove("/baddir"); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); - -testPerm({ write: false }, async function removePerm(): Promise<void> { - let err; - try { - await Deno.remove("/baddir"); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -testPerm({ write: true }, async function removeAllDirSuccess(): Promise<void> { - // REMOVE EMPTY DIRECTORY - let path = Deno.makeTempDirSync() + "/dir/subdir"; - Deno.mkdirSync(path); - let pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory()); // check exist first - await Deno.remove(path, { recursive: true }); // remove - // We then check again after remove - let err; - try { - Deno.statSync(path); - } catch (e) { - err = e; - } - // Directory is gone - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); - // REMOVE NON-EMPTY DIRECTORY - path = Deno.makeTempDirSync() + "/dir/subdir"; - const subPath = path + "/subsubdir"; - Deno.mkdirSync(path); - Deno.mkdirSync(subPath); - pathInfo = Deno.statSync(path); - assert(pathInfo.isDirectory()); // check exist first - const subPathInfo = Deno.statSync(subPath); - assert(subPathInfo.isDirectory()); // check exist first - await Deno.remove(path, { recursive: true }); // remove - // We then check parent directory again after remove - try { - Deno.statSync(path); - } catch (e) { - err = e; - } - // Directory is gone - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); - -testPerm({ write: true }, async function removeAllFileSuccess(): Promise<void> { - // REMOVE FILE - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - Deno.writeFileSync(filename, data, { perm: 0o666 }); - const fileInfo = Deno.statSync(filename); - assert(fileInfo.isFile()); // check exist first - await Deno.remove(filename, { recursive: true }); // remove - // We then check again after remove - let err; - try { - Deno.statSync(filename); - } catch (e) { - err = e; - } - // File is gone - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); - -testPerm({ write: true }, async function removeAllFail(): Promise<void> { - // NON-EXISTENT DIRECTORY/FILE - let err; - try { - // Non-existent - await Deno.remove("/baddir", { recursive: true }); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); -}); - -testPerm({ write: false }, async function removeAllPerm(): Promise<void> { - let err; - try { - await Deno.remove("/baddir", { recursive: true }); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); diff --git a/js/rename.ts b/js/rename.ts deleted file mode 100644 index c906ce37b..000000000 --- a/js/rename.ts +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendSync, sendAsync } from "./dispatch_json.ts"; -import * as dispatch from "./dispatch.ts"; - -/** Synchronously renames (moves) `oldpath` to `newpath`. If `newpath` already - * exists and is not a directory, `renameSync()` replaces it. OS-specific - * restrictions may apply when `oldpath` and `newpath` are in different - * directories. - * - * Deno.renameSync("old/path", "new/path"); - */ -export function renameSync(oldpath: string, newpath: string): void { - sendSync(dispatch.OP_RENAME, { oldpath, newpath }); -} - -/** Renames (moves) `oldpath` to `newpath`. If `newpath` already exists and is - * not a directory, `rename()` replaces it. OS-specific restrictions may apply - * when `oldpath` and `newpath` are in different directories. - * - * await Deno.rename("old/path", "new/path"); - */ -export async function rename(oldpath: string, newpath: string): Promise<void> { - await sendAsync(dispatch.OP_RENAME, { oldpath, newpath }); -} diff --git a/js/rename_test.ts b/js/rename_test.ts deleted file mode 100644 index 43d02d419..000000000 --- a/js/rename_test.ts +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { testPerm, assert, assertEquals } from "./test_util.ts"; - -testPerm({ read: true, write: true }, function renameSyncSuccess(): void { - const testDir = Deno.makeTempDirSync(); - const oldpath = testDir + "/oldpath"; - const newpath = testDir + "/newpath"; - Deno.mkdirSync(oldpath); - Deno.renameSync(oldpath, newpath); - const newPathInfo = Deno.statSync(newpath); - assert(newPathInfo.isDirectory()); - - let caughtErr = false; - let oldPathInfo; - - try { - oldPathInfo = Deno.statSync(oldpath); - } catch (e) { - caughtErr = true; - assertEquals(e.kind, Deno.ErrorKind.NotFound); - } - assert(caughtErr); - assertEquals(oldPathInfo, undefined); -}); - -testPerm({ read: false, write: true }, function renameSyncReadPerm(): void { - let err; - try { - const oldpath = "/oldbaddir"; - const newpath = "/newbaddir"; - Deno.renameSync(oldpath, newpath); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -testPerm({ read: true, write: false }, function renameSyncWritePerm(): void { - let err; - try { - const oldpath = "/oldbaddir"; - const newpath = "/newbaddir"; - Deno.renameSync(oldpath, newpath); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -testPerm({ read: true, write: true }, async function renameSuccess(): Promise< - void -> { - const testDir = Deno.makeTempDirSync(); - const oldpath = testDir + "/oldpath"; - const newpath = testDir + "/newpath"; - Deno.mkdirSync(oldpath); - await Deno.rename(oldpath, newpath); - const newPathInfo = Deno.statSync(newpath); - assert(newPathInfo.isDirectory()); - - let caughtErr = false; - let oldPathInfo; - - try { - oldPathInfo = Deno.statSync(oldpath); - } catch (e) { - caughtErr = true; - assertEquals(e.kind, Deno.ErrorKind.NotFound); - } - assert(caughtErr); - assertEquals(oldPathInfo, undefined); -}); diff --git a/js/repl.ts b/js/repl.ts deleted file mode 100644 index 966e809e8..000000000 --- a/js/repl.ts +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { close } from "./files.ts"; -import { exit } from "./os.ts"; -import { window } from "./window.ts"; -import { core } from "./core.ts"; -import { formatError } from "./format_error.ts"; -import { stringifyArgs } from "./console.ts"; -import * as dispatch from "./dispatch.ts"; -import { sendSync, sendAsync } from "./dispatch_json.ts"; - -const { console } = window; - -/** - * REPL logging. - * In favor of console.log to avoid unwanted indentation - */ -function replLog(...args: unknown[]): void { - core.print(stringifyArgs(args) + "\n"); -} - -/** - * REPL logging for errors. - * In favor of console.error to avoid unwanted indentation - */ -function replError(...args: unknown[]): void { - core.print(stringifyArgs(args) + "\n", true); -} - -const helpMsg = [ - "_ Get last evaluation result", - "_error Get last thrown error", - "exit Exit the REPL", - "help Print this help message" -].join("\n"); - -const replCommands = { - exit: { - get(): void { - exit(0); - } - }, - help: { - get(): string { - return helpMsg; - } - } -}; - -function startRepl(historyFile: string): number { - return sendSync(dispatch.OP_REPL_START, { historyFile }); -} - -// @internal -export async function readline(rid: number, prompt: string): Promise<string> { - return sendAsync(dispatch.OP_REPL_READLINE, { rid, prompt }); -} - -// Error messages that allow users to continue input -// instead of throwing an error to REPL -// ref: https://github.com/v8/v8/blob/master/src/message-template.h -// TODO(kevinkassimo): this list might not be comprehensive -const recoverableErrorMessages = [ - "Unexpected end of input", // { or [ or ( - "Missing initializer in const declaration", // const a - "Missing catch or finally after try", // try {} - "missing ) after argument list", // console.log(1 - "Unterminated template literal" // `template - // TODO(kevinkassimo): need a parser to handling errors such as: - // "Missing } in template expression" // `${ or `${ a 123 }` -]; - -function isRecoverableError(e: Error): boolean { - return recoverableErrorMessages.includes(e.message); -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type Value = any; - -let lastEvalResult: Value = undefined; -let lastThrownError: Value = undefined; - -// Evaluate code. -// Returns true if code is consumed (no error/irrecoverable error). -// Returns false if error is recoverable -function evaluate(code: string): boolean { - const [result, errInfo] = core.evalContext(code); - if (!errInfo) { - lastEvalResult = result; - replLog(result); - } else if (errInfo.isCompileError && isRecoverableError(errInfo.thrown)) { - // Recoverable compiler error - return false; // don't consume code. - } else { - lastThrownError = errInfo.thrown; - if (errInfo.isNativeError) { - const formattedError = formatError( - core.errorToJSON(errInfo.thrown as Error) - ); - replError(formattedError); - } else { - replError("Thrown:", errInfo.thrown); - } - } - return true; -} - -// @internal -export async function replLoop(): Promise<void> { - Object.defineProperties(window, replCommands); - - const historyFile = "deno_history.txt"; - const rid = startRepl(historyFile); - - const quitRepl = (exitCode: number): void => { - // Special handling in case user calls deno.close(3). - try { - close(rid); // close signals Drop on REPL and saves history. - } catch {} - exit(exitCode); - }; - - // Configure window._ to give the last evaluation result. - Object.defineProperty(window, "_", { - configurable: true, - get: (): Value => lastEvalResult, - set: (value: Value): Value => { - Object.defineProperty(window, "_", { - value: value, - writable: true, - enumerable: true, - configurable: true - }); - console.log("Last evaluation result is no longer saved to _."); - } - }); - - // Configure window._error to give the last thrown error. - Object.defineProperty(window, "_error", { - configurable: true, - get: (): Value => lastThrownError, - set: (value: Value): Value => { - Object.defineProperty(window, "_error", { - value: value, - writable: true, - enumerable: true, - configurable: true - }); - console.log("Last thrown error is no longer saved to _error."); - } - }); - - while (true) { - let code = ""; - // Top level read - try { - code = await readline(rid, "> "); - if (code.trim() === "") { - continue; - } - } catch (err) { - if (err.message === "EOF") { - quitRepl(0); - } else { - // If interrupted, don't print error. - if (err.message !== "Interrupted") { - // e.g. this happens when we have deno.close(3). - // We want to display the problem. - const formattedError = formatError(core.errorToJSON(err)); - replError(formattedError); - } - // Quit REPL anyways. - quitRepl(1); - } - } - // Start continued read - while (!evaluate(code)) { - code += "\n"; - try { - code += await readline(rid, " "); - } catch (err) { - // If interrupted on continued read, - // abort this read instead of quitting. - if (err.message === "Interrupted") { - break; - } else if (err.message === "EOF") { - quitRepl(0); - } else { - // e.g. this happens when we have deno.close(3). - // We want to display the problem. - const formattedError = formatError(core.errorToJSON(err)); - replError(formattedError); - quitRepl(1); - } - } - } - } -} diff --git a/js/request.ts b/js/request.ts deleted file mode 100644 index 0c77b8854..000000000 --- a/js/request.ts +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as headers from "./headers.ts"; -import * as body from "./body.ts"; -import * as domTypes from "./dom_types.ts"; - -const { Headers } = headers; - -function byteUpperCase(s: string): string { - return String(s).replace(/[a-z]/g, function byteUpperCaseReplace(c): string { - return c.toUpperCase(); - }); -} - -function normalizeMethod(m: string): string { - const u = byteUpperCase(m); - if ( - u === "DELETE" || - u === "GET" || - u === "HEAD" || - u === "OPTIONS" || - u === "POST" || - u === "PUT" - ) { - return u; - } - return m; -} - -/** - * An HTTP request - * @param {Blob|String} [body] - * @param {Object} [init] - */ -export class Request extends body.Body implements domTypes.Request { - public method: string; - public url: string; - public credentials?: "omit" | "same-origin" | "include"; - public headers: domTypes.Headers; - - constructor(input: domTypes.RequestInfo, init?: domTypes.RequestInit) { - if (arguments.length < 1) { - throw TypeError("Not enough arguments"); - } - - if (!init) { - init = {}; - } - - let b: body.BodySource; - - // prefer body from init - if (init.body) { - b = init.body; - } else if (input instanceof Request && input._bodySource) { - if (input.bodyUsed) { - throw TypeError(body.BodyUsedError); - } - b = input._bodySource; - } else if (typeof input === "object" && "body" in input && input.body) { - if (input.bodyUsed) { - throw TypeError(body.BodyUsedError); - } - b = input.body; - } else { - b = ""; - } - - let headers: domTypes.Headers; - - // prefer headers from init - if (init.headers) { - headers = new Headers(init.headers); - } else if (input instanceof Request) { - headers = input.headers; - } else { - headers = new Headers(); - } - - const contentType = headers.get("content-type") || ""; - super(b, contentType); - this.headers = headers; - - // readonly attribute ByteString method; - /** - * The HTTP request method - * @readonly - * @default GET - * @type {string} - */ - this.method = "GET"; - - // readonly attribute USVString url; - /** - * The request URL - * @readonly - * @type {string} - */ - this.url = ""; - - // readonly attribute RequestCredentials credentials; - this.credentials = "omit"; - - if (input instanceof Request) { - if (input.bodyUsed) { - throw TypeError(body.BodyUsedError); - } - this.method = input.method; - this.url = input.url; - this.headers = new Headers(input.headers); - this.credentials = input.credentials; - this._stream = input._stream; - } else if (typeof input === "string") { - this.url = input; - } - - if (init && "method" in init) { - this.method = normalizeMethod(init.method as string); - } - - if ( - init && - "credentials" in init && - init.credentials && - ["omit", "same-origin", "include"].indexOf(init.credentials) !== -1 - ) { - this.credentials = init.credentials; - } - } - - public clone(): domTypes.Request { - if (this.bodyUsed) { - throw TypeError(body.BodyUsedError); - } - - const iterators = this.headers.entries(); - const headersList: Array<[string, string]> = []; - for (const header of iterators) { - headersList.push(header); - } - - const body2 = this._bodySource; - - const cloned = new Request(this.url, { - body: body2, - method: this.method, - headers: new Headers(headersList), - credentials: this.credentials - }); - return cloned; - } -} diff --git a/js/request_test.ts b/js/request_test.ts deleted file mode 100644 index e9e1f5164..000000000 --- a/js/request_test.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, assertEquals } from "./test_util.ts"; - -test(function fromInit(): void { - const req = new Request("https://example.com", { - body: "ahoyhoy", - method: "POST", - headers: { - "test-header": "value" - } - }); - - // @ts-ignore - assertEquals("ahoyhoy", req._bodySource); - assertEquals(req.url, "https://example.com"); - assertEquals(req.headers.get("test-header"), "value"); -}); diff --git a/js/resources.ts b/js/resources.ts deleted file mode 100644 index 27598ce09..000000000 --- a/js/resources.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as dispatch from "./dispatch.ts"; -import { sendSync } from "./dispatch_json.ts"; - -export interface ResourceMap { - [rid: number]: string; -} - -/** Returns a map of open _file like_ resource ids along with their string - * representation. - */ -export function resources(): ResourceMap { - const res = sendSync(dispatch.OP_RESOURCES) as Array<[number, string]>; - const resources: ResourceMap = {}; - for (const resourceTuple of res) { - resources[resourceTuple[0]] = resourceTuple[1]; - } - return resources; -} diff --git a/js/resources_test.ts b/js/resources_test.ts deleted file mode 100644 index 753ef3e17..000000000 --- a/js/resources_test.ts +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, testPerm, assertEquals } from "./test_util.ts"; - -test(function resourcesStdio(): void { - const res = Deno.resources(); - - assertEquals(res[0], "stdin"); - assertEquals(res[1], "stdout"); - assertEquals(res[2], "stderr"); -}); - -testPerm({ net: true }, async function resourcesNet(): Promise<void> { - const listener = Deno.listen({ port: 4501 }); - const dialerConn = await Deno.dial({ port: 4501 }); - const listenerConn = await listener.accept(); - - const res = Deno.resources(); - assertEquals( - Object.values(res).filter((r): boolean => r === "tcpListener").length, - 1 - ); - assertEquals( - Object.values(res).filter((r): boolean => r === "tcpStream").length, - 2 - ); - - listenerConn.close(); - dialerConn.close(); - listener.close(); -}); - -testPerm({ read: true }, async function resourcesFile(): Promise<void> { - const resourcesBefore = Deno.resources(); - await Deno.open("tests/hello.txt"); - const resourcesAfter = Deno.resources(); - - // check that exactly one new resource (file) was added - assertEquals( - Object.keys(resourcesAfter).length, - Object.keys(resourcesBefore).length + 1 - ); - const newRid = Object.keys(resourcesAfter).find( - (rid): boolean => { - return !resourcesBefore.hasOwnProperty(rid); - } - ); - assertEquals(resourcesAfter[newRid], "fsFile"); -}); diff --git a/js/stat.ts b/js/stat.ts deleted file mode 100644 index 1f53e6f7b..000000000 --- a/js/stat.ts +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendSync, sendAsync } from "./dispatch_json.ts"; -import * as dispatch from "./dispatch.ts"; -import { FileInfo, FileInfoImpl } from "./file_info.ts"; - -export interface StatResponse { - isFile: boolean; - isSymlink: boolean; - len: number; - modified: number; - accessed: number; - created: number; - mode: number; - hasMode: boolean; // false on windows - name: string | null; -} - -/** Queries the file system for information on the path provided. If the given - * path is a symlink information about the symlink will be returned. - * - * const fileInfo = await Deno.lstat("hello.txt"); - * assert(fileInfo.isFile()); - */ -export async function lstat(filename: string): Promise<FileInfo> { - const res = (await sendAsync(dispatch.OP_STAT, { - filename, - lstat: true - })) as StatResponse; - return new FileInfoImpl(res); -} - -/** Queries the file system for information on the path provided synchronously. - * If the given path is a symlink information about the symlink will be - * returned. - * - * const fileInfo = Deno.lstatSync("hello.txt"); - * assert(fileInfo.isFile()); - */ -export function lstatSync(filename: string): FileInfo { - const res = sendSync(dispatch.OP_STAT, { - filename, - lstat: true - }) as StatResponse; - return new FileInfoImpl(res); -} - -/** Queries the file system for information on the path provided. `stat` Will - * always follow symlinks. - * - * const fileInfo = await Deno.stat("hello.txt"); - * assert(fileInfo.isFile()); - */ -export async function stat(filename: string): Promise<FileInfo> { - const res = (await sendAsync(dispatch.OP_STAT, { - filename, - lstat: false - })) as StatResponse; - return new FileInfoImpl(res); -} - -/** Queries the file system for information on the path provided synchronously. - * `statSync` Will always follow symlinks. - * - * const fileInfo = Deno.statSync("hello.txt"); - * assert(fileInfo.isFile()); - */ -export function statSync(filename: string): FileInfo { - const res = sendSync(dispatch.OP_STAT, { - filename, - lstat: false - }) as StatResponse; - return new FileInfoImpl(res); -} diff --git a/js/stat_test.ts b/js/stat_test.ts deleted file mode 100644 index 1542f1080..000000000 --- a/js/stat_test.ts +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { testPerm, assert, assertEquals } from "./test_util.ts"; - -// TODO Add tests for modified, accessed, and created fields once there is a way -// to create temp files. -testPerm({ read: true }, async function statSyncSuccess(): Promise<void> { - const packageInfo = Deno.statSync("package.json"); - assert(packageInfo.isFile()); - assert(!packageInfo.isSymlink()); - - const modulesInfo = Deno.statSync("node_modules"); - assert(modulesInfo.isDirectory()); - assert(!modulesInfo.isSymlink()); - - const testsInfo = Deno.statSync("tests"); - assert(testsInfo.isDirectory()); - assert(!testsInfo.isSymlink()); -}); - -testPerm({ read: false }, async function statSyncPerm(): Promise<void> { - let caughtError = false; - try { - Deno.statSync("package.json"); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); - -testPerm({ read: true }, async function statSyncNotFound(): Promise<void> { - let caughtError = false; - let badInfo; - - try { - badInfo = Deno.statSync("bad_file_name"); - } catch (err) { - caughtError = true; - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); - } - - assert(caughtError); - assertEquals(badInfo, undefined); -}); - -testPerm({ read: true }, async function lstatSyncSuccess(): Promise<void> { - const packageInfo = Deno.lstatSync("package.json"); - assert(packageInfo.isFile()); - assert(!packageInfo.isSymlink()); - - const modulesInfo = Deno.lstatSync("node_modules"); - assert(!modulesInfo.isDirectory()); - assert(modulesInfo.isSymlink()); - - const i = Deno.lstatSync("website"); - assert(i.isDirectory()); - assert(!i.isSymlink()); -}); - -testPerm({ read: false }, async function lstatSyncPerm(): Promise<void> { - let caughtError = false; - try { - Deno.lstatSync("package.json"); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); - -testPerm({ read: true }, async function lstatSyncNotFound(): Promise<void> { - let caughtError = false; - let badInfo; - - try { - badInfo = Deno.lstatSync("bad_file_name"); - } catch (err) { - caughtError = true; - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); - } - - assert(caughtError); - assertEquals(badInfo, undefined); -}); - -testPerm({ read: true }, async function statSuccess(): Promise<void> { - const packageInfo = await Deno.stat("package.json"); - assert(packageInfo.isFile()); - assert(!packageInfo.isSymlink()); - - const modulesInfo = await Deno.stat("node_modules"); - assert(modulesInfo.isDirectory()); - assert(!modulesInfo.isSymlink()); - - const i = await Deno.stat("tests"); - assert(i.isDirectory()); - assert(!i.isSymlink()); -}); - -testPerm({ read: false }, async function statPerm(): Promise<void> { - let caughtError = false; - try { - await Deno.stat("package.json"); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); - -testPerm({ read: true }, async function statNotFound(): Promise<void> { - let caughtError = false; - let badInfo; - - try { - badInfo = await Deno.stat("bad_file_name"); - } catch (err) { - caughtError = true; - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); - } - - assert(caughtError); - assertEquals(badInfo, undefined); -}); - -testPerm({ read: true }, async function lstatSuccess(): Promise<void> { - const packageInfo = await Deno.lstat("package.json"); - assert(packageInfo.isFile()); - assert(!packageInfo.isSymlink()); - - const modulesInfo = await Deno.lstat("node_modules"); - assert(!modulesInfo.isDirectory()); - assert(modulesInfo.isSymlink()); - - const i = await Deno.lstat("website"); - assert(i.isDirectory()); - assert(!i.isSymlink()); -}); - -testPerm({ read: false }, async function lstatPerm(): Promise<void> { - let caughtError = false; - try { - await Deno.lstat("package.json"); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); - -testPerm({ read: true }, async function lstatNotFound(): Promise<void> { - let caughtError = false; - let badInfo; - - try { - badInfo = await Deno.lstat("bad_file_name"); - } catch (err) { - caughtError = true; - assertEquals(err.kind, Deno.ErrorKind.NotFound); - assertEquals(err.name, "NotFound"); - } - - assert(caughtError); - assertEquals(badInfo, undefined); -}); diff --git a/js/symlink.ts b/js/symlink.ts deleted file mode 100644 index 21ebb2f59..000000000 --- a/js/symlink.ts +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendSync, sendAsync } from "./dispatch_json.ts"; -import * as dispatch from "./dispatch.ts"; -import * as util from "./util.ts"; -import { build } from "./build.ts"; - -/** Synchronously creates `newname` as a symbolic link to `oldname`. The type - * argument can be set to `dir` or `file` and is only available on Windows - * (ignored on other platforms). - * - * Deno.symlinkSync("old/name", "new/name"); - */ -export function symlinkSync( - oldname: string, - newname: string, - type?: string -): void { - if (build.os === "win" && type) { - return util.notImplemented(); - } - sendSync(dispatch.OP_SYMLINK, { oldname, newname }); -} - -/** Creates `newname` as a symbolic link to `oldname`. The type argument can be - * set to `dir` or `file` and is only available on Windows (ignored on other - * platforms). - * - * await Deno.symlink("old/name", "new/name"); - */ -export async function symlink( - oldname: string, - newname: string, - type?: string -): Promise<void> { - if (build.os === "win" && type) { - return util.notImplemented(); - } - await sendAsync(dispatch.OP_SYMLINK, { oldname, newname }); -} diff --git a/js/symlink_test.ts b/js/symlink_test.ts deleted file mode 100644 index bce1f6ae5..000000000 --- a/js/symlink_test.ts +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, testPerm, assert, assertEquals } from "./test_util.ts"; - -testPerm({ read: true, write: true }, function symlinkSyncSuccess(): void { - const testDir = Deno.makeTempDirSync(); - const oldname = testDir + "/oldname"; - const newname = testDir + "/newname"; - Deno.mkdirSync(oldname); - let errOnWindows; - // Just for now, until we implement symlink for Windows. - try { - Deno.symlinkSync(oldname, newname); - } catch (e) { - errOnWindows = e; - } - if (errOnWindows) { - assertEquals(Deno.build.os, "win"); - assertEquals(errOnWindows.kind, Deno.ErrorKind.Other); - assertEquals(errOnWindows.message, "Not implemented"); - } else { - const newNameInfoLStat = Deno.lstatSync(newname); - const newNameInfoStat = Deno.statSync(newname); - assert(newNameInfoLStat.isSymlink()); - assert(newNameInfoStat.isDirectory()); - } -}); - -test(function symlinkSyncPerm(): void { - let err; - try { - Deno.symlinkSync("oldbaddir", "newbaddir"); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -// Just for now, until we implement symlink for Windows. -// Symlink with type should succeed on other platforms with type ignored -testPerm({ write: true }, function symlinkSyncNotImplemented(): void { - const testDir = Deno.makeTempDirSync(); - const oldname = testDir + "/oldname"; - const newname = testDir + "/newname"; - let err; - try { - Deno.symlinkSync(oldname, newname, "dir"); - } catch (e) { - err = e; - } - if (err) { - assertEquals(Deno.build.os, "win"); - assertEquals(err.message, "Not implemented"); - } -}); - -testPerm({ read: true, write: true }, async function symlinkSuccess(): Promise< - void -> { - const testDir = Deno.makeTempDirSync(); - const oldname = testDir + "/oldname"; - const newname = testDir + "/newname"; - Deno.mkdirSync(oldname); - let errOnWindows; - // Just for now, until we implement symlink for Windows. - try { - await Deno.symlink(oldname, newname); - } catch (e) { - errOnWindows = e; - } - if (errOnWindows) { - assertEquals(errOnWindows.kind, Deno.ErrorKind.Other); - assertEquals(errOnWindows.message, "Not implemented"); - } else { - const newNameInfoLStat = Deno.lstatSync(newname); - const newNameInfoStat = Deno.statSync(newname); - assert(newNameInfoLStat.isSymlink()); - assert(newNameInfoStat.isDirectory()); - } -}); diff --git a/js/test_util.ts b/js/test_util.ts deleted file mode 100644 index bb75082e6..000000000 --- a/js/test_util.ts +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -// -// We want to test many ops in deno which have different behavior depending on -// the permissions set. These tests can specify which permissions they expect, -// which appends a special string like "permW1N0" to the end of the test name. -// Here we run several copies of deno with different permissions, filtering the -// tests by the special string. permW1N0 means allow-write but not allow-net. -// See tools/unit_tests.py for more details. - -import * as testing from "../std/testing/mod.ts"; -import { assert, assertEquals } from "../std/testing/asserts.ts"; -export { - assert, - assertThrows, - assertEquals, - assertMatch, - assertNotEquals, - assertStrictEq, - assertStrContains, - unreachable -} from "../std/testing/asserts.ts"; - -interface TestPermissions { - read?: boolean; - write?: boolean; - net?: boolean; - env?: boolean; - run?: boolean; - hrtime?: boolean; -} - -const processPerms = Deno.permissions(); - -function permissionsMatch( - processPerms: Deno.Permissions, - requiredPerms: Deno.Permissions -): boolean { - for (const permName in processPerms) { - if (processPerms[permName] !== requiredPerms[permName]) { - return false; - } - } - - return true; -} - -export const permissionCombinations: Map<string, Deno.Permissions> = new Map(); - -function permToString(perms: Deno.Permissions): string { - const r = perms.read ? 1 : 0; - const w = perms.write ? 1 : 0; - const n = perms.net ? 1 : 0; - const e = perms.env ? 1 : 0; - const u = perms.run ? 1 : 0; - const h = perms.hrtime ? 1 : 0; - return `permR${r}W${w}N${n}E${e}U${u}H${h}`; -} - -function registerPermCombination(perms: Deno.Permissions): void { - const key = permToString(perms); - if (!permissionCombinations.has(key)) { - permissionCombinations.set(key, perms); - } -} - -function normalizeTestPermissions(perms: TestPermissions): Deno.Permissions { - return { - read: !!perms.read, - write: !!perms.write, - net: !!perms.net, - run: !!perms.run, - env: !!perms.env, - hrtime: !!perms.hrtime - }; -} - -export function testPerm( - perms: TestPermissions, - fn: testing.TestFunction -): void { - const normalizedPerms = normalizeTestPermissions(perms); - - registerPermCombination(normalizedPerms); - - if (!permissionsMatch(processPerms, normalizedPerms)) { - return; - } - - testing.test(fn); -} - -export function test(fn: testing.TestFunction): void { - testPerm( - { - read: false, - write: false, - net: false, - env: false, - run: false, - hrtime: false - }, - fn - ); -} - -function extractNumber(re: RegExp, str: string): number | undefined { - const match = str.match(re); - - if (match) { - return Number.parseInt(match[1]); - } -} - -export function parseUnitTestOutput( - rawOutput: Uint8Array, - print: boolean -): { actual?: number; expected?: number; resultOutput?: string } { - const decoder = new TextDecoder(); - const output = decoder.decode(rawOutput); - - let expected, actual, result; - - for (const line of output.split("\n")) { - if (!expected) { - // expect "running 30 tests" - expected = extractNumber(/running (\d+) tests/, line); - } else if (line.indexOf("test result:") !== -1) { - result = line; - } - - if (print) { - console.log(line); - } - } - - // Check that the number of expected tests equals what was reported at the - // bottom. - if (result) { - // result should be a string like this: - // "test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; ..." - actual = extractNumber(/(\d+) passed/, result); - } - - return { actual, expected, resultOutput: result }; -} - -test(function permissionsMatches(): void { - assert( - permissionsMatch( - { - read: true, - write: false, - net: false, - env: false, - run: false, - hrtime: false - }, - normalizeTestPermissions({ read: true }) - ) - ); - - assert( - permissionsMatch( - { - read: false, - write: false, - net: false, - env: false, - run: false, - hrtime: false - }, - normalizeTestPermissions({}) - ) - ); - - assertEquals( - permissionsMatch( - { - read: false, - write: true, - net: true, - env: true, - run: true, - hrtime: true - }, - normalizeTestPermissions({ read: true }) - ), - false - ); - - assertEquals( - permissionsMatch( - { - read: true, - write: false, - net: true, - env: false, - run: false, - hrtime: false - }, - normalizeTestPermissions({ read: true }) - ), - false - ); - - assert( - permissionsMatch( - { - read: true, - write: true, - net: true, - env: true, - run: true, - hrtime: true - }, - { - read: true, - write: true, - net: true, - env: true, - run: true, - hrtime: true - } - ) - ); -}); - -testPerm({ read: true }, async function parsingUnitTestOutput(): Promise<void> { - const cwd = Deno.cwd(); - const testDataPath = `${cwd}/tools/testdata/`; - - let result; - - // This is an example of a successful unit test output. - result = parseUnitTestOutput( - await Deno.readFile(`${testDataPath}/unit_test_output1.txt`), - false - ); - assertEquals(result.actual, 96); - assertEquals(result.expected, 96); - - // This is an example of a silently dying unit test. - result = parseUnitTestOutput( - await Deno.readFile(`${testDataPath}/unit_test_output2.txt`), - false - ); - assertEquals(result.actual, undefined); - assertEquals(result.expected, 96); - - // This is an example of compiling before successful unit tests. - result = parseUnitTestOutput( - await Deno.readFile(`${testDataPath}/unit_test_output3.txt`), - false - ); - assertEquals(result.actual, 96); - assertEquals(result.expected, 96); - - // Check what happens on empty output. - result = parseUnitTestOutput(new TextEncoder().encode("\n\n\n"), false); - assertEquals(result.actual, undefined); - assertEquals(result.expected, undefined); -}); diff --git a/js/text_encoding.ts b/js/text_encoding.ts deleted file mode 100644 index 8386ff8b0..000000000 --- a/js/text_encoding.ts +++ /dev/null @@ -1,554 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -// The following code is based off of text-encoding at: -// https://github.com/inexorabletash/text-encoding -// -// Anyone is free to copy, modify, publish, use, compile, sell, or -// distribute this software, either in source code form or as a compiled -// binary, for any purpose, commercial or non-commercial, and by any -// means. -// -// In jurisdictions that recognize copyright laws, the author or authors -// of this software dedicate any and all copyright interest in the -// software to the public domain. We make this dedication for the benefit -// of the public at large and to the detriment of our heirs and -// successors. We intend this dedication to be an overt act of -// relinquishment in perpetuity of all present and future rights to this -// software under copyright law. -// -// 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 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 * as base64 from "./base64.ts"; -import * as domTypes from "./dom_types.ts"; -import { DenoError, ErrorKind } from "./errors.ts"; - -const CONTINUE = null; -const END_OF_STREAM = -1; -const FINISHED = -1; - -function decoderError(fatal: boolean): number | never { - if (fatal) { - throw new TypeError("Decoder error."); - } - return 0xfffd; // default code point -} - -function inRange(a: number, min: number, max: number): boolean { - return min <= a && a <= max; -} - -function isASCIIByte(a: number): boolean { - return inRange(a, 0x00, 0x7f); -} - -function stringToCodePoints(input: string): number[] { - const u: number[] = []; - for (const c of input) { - u.push(c.codePointAt(0)!); - } - return u; -} - -class UTF8Decoder implements Decoder { - private _codePoint = 0; - private _bytesSeen = 0; - private _bytesNeeded = 0; - private _fatal: boolean; - private _ignoreBOM: boolean; - private _lowerBoundary = 0x80; - private _upperBoundary = 0xbf; - - constructor(options: DecoderOptions) { - this._fatal = options.fatal || false; - this._ignoreBOM = options.ignoreBOM || false; - } - - handler(stream: Stream, byte: number): number | null { - if (byte === END_OF_STREAM && this._bytesNeeded !== 0) { - this._bytesNeeded = 0; - return decoderError(this._fatal); - } - - if (byte === END_OF_STREAM) { - return FINISHED; - } - - if (this._ignoreBOM) { - if ( - (this._bytesSeen === 0 && byte !== 0xef) || - (this._bytesSeen === 1 && byte !== 0xbb) - ) { - this._ignoreBOM = false; - } - - if (this._bytesSeen === 2) { - this._ignoreBOM = false; - if (byte === 0xbf) { - //Ignore BOM - this._codePoint = 0; - this._bytesNeeded = 0; - this._bytesSeen = 0; - return CONTINUE; - } - } - } - - if (this._bytesNeeded === 0) { - if (isASCIIByte(byte)) { - // Single byte code point - return byte; - } else if (inRange(byte, 0xc2, 0xdf)) { - // Two byte code point - this._bytesNeeded = 1; - this._codePoint = byte & 0x1f; - } else if (inRange(byte, 0xe0, 0xef)) { - // Three byte code point - if (byte === 0xe0) { - this._lowerBoundary = 0xa0; - } else if (byte === 0xed) { - this._upperBoundary = 0x9f; - } - this._bytesNeeded = 2; - this._codePoint = byte & 0xf; - } else if (inRange(byte, 0xf0, 0xf4)) { - if (byte === 0xf0) { - this._lowerBoundary = 0x90; - } else if (byte === 0xf4) { - this._upperBoundary = 0x8f; - } - this._bytesNeeded = 3; - this._codePoint = byte & 0x7; - } else { - return decoderError(this._fatal); - } - return CONTINUE; - } - - if (!inRange(byte, this._lowerBoundary, this._upperBoundary)) { - // Byte out of range, so encoding error - this._codePoint = 0; - this._bytesNeeded = 0; - this._bytesSeen = 0; - stream.prepend(byte); - return decoderError(this._fatal); - } - - this._lowerBoundary = 0x80; - this._upperBoundary = 0xbf; - - this._codePoint = (this._codePoint << 6) | (byte & 0x3f); - - this._bytesSeen++; - - if (this._bytesSeen !== this._bytesNeeded) { - return CONTINUE; - } - - const codePoint = this._codePoint; - - this._codePoint = 0; - this._bytesNeeded = 0; - this._bytesSeen = 0; - - return codePoint; - } -} - -class UTF8Encoder implements Encoder { - handler(codePoint: number): number | number[] { - if (codePoint === END_OF_STREAM) { - return FINISHED; - } - - if (inRange(codePoint, 0x00, 0x7f)) { - return codePoint; - } - - let count: number; - let offset: number; - if (inRange(codePoint, 0x0080, 0x07ff)) { - count = 1; - offset = 0xc0; - } else if (inRange(codePoint, 0x0800, 0xffff)) { - count = 2; - offset = 0xe0; - } else if (inRange(codePoint, 0x10000, 0x10ffff)) { - count = 3; - offset = 0xf0; - } else { - throw TypeError(`Code point out of range: \\x${codePoint.toString(16)}`); - } - - const bytes = [(codePoint >> (6 * count)) + offset]; - - while (count > 0) { - const temp = codePoint >> (6 * (count - 1)); - bytes.push(0x80 | (temp & 0x3f)); - count--; - } - - return bytes; - } -} - -/** Decodes a string of data which has been encoded using base-64. */ -export function atob(s: string): string { - s = String(s); - s = s.replace(/[\t\n\f\r ]/g, ""); - - if (s.length % 4 === 0) { - s = s.replace(/==?$/, ""); - } - - const rem = s.length % 4; - if (rem === 1 || /[^+/0-9A-Za-z]/.test(s)) { - // TODO: throw `DOMException` - throw new DenoError( - ErrorKind.InvalidInput, - "The string to be decoded is not correctly encoded" - ); - } - - // base64-js requires length exactly times of 4 - if (rem > 0) { - s = s.padEnd(s.length + (4 - rem), "="); - } - - const byteArray: Uint8Array = base64.toByteArray(s); - let result = ""; - for (let i = 0; i < byteArray.length; i++) { - result += String.fromCharCode(byteArray[i]); - } - return result; -} - -/** Creates a base-64 ASCII string from the input string. */ -export function btoa(s: string): string { - const byteArray = []; - for (let i = 0; i < s.length; i++) { - const charCode = s[i].charCodeAt(0); - if (charCode > 0xff) { - throw new DenoError( - ErrorKind.InvalidInput, - "The string to be encoded contains characters " + - "outside of the Latin1 range." - ); - } - byteArray.push(charCode); - } - const result = base64.fromByteArray(Uint8Array.from(byteArray)); - return result; -} - -interface DecoderOptions { - fatal?: boolean; - ignoreBOM?: boolean; -} - -interface Decoder { - handler(stream: Stream, byte: number): number | null; -} - -interface Encoder { - handler(codePoint: number): number | number[]; -} - -class SingleByteDecoder implements Decoder { - private _index: number[]; - private _fatal: boolean; - - constructor(index: number[], options: DecoderOptions) { - if (options.ignoreBOM) { - throw new TypeError("Ignoring the BOM is available only with utf-8."); - } - this._fatal = options.fatal || false; - this._index = index; - } - handler(stream: Stream, byte: number): number { - if (byte === END_OF_STREAM) { - return FINISHED; - } - if (isASCIIByte(byte)) { - return byte; - } - const codePoint = this._index[byte - 0x80]; - - if (codePoint == null) { - return decoderError(this._fatal); - } - - return codePoint; - } -} - -// The encodingMap is a hash of labels that are indexed by the conical -// encoding. -const encodingMap: { [key: string]: string[] } = { - "windows-1252": [ - "ansi_x3.4-1968", - "ascii", - "cp1252", - "cp819", - "csisolatin1", - "ibm819", - "iso-8859-1", - "iso-ir-100", - "iso8859-1", - "iso88591", - "iso_8859-1", - "iso_8859-1:1987", - "l1", - "latin1", - "us-ascii", - "windows-1252", - "x-cp1252" - ], - "utf-8": ["unicode-1-1-utf-8", "utf-8", "utf8"] -}; -// We convert these into a Map where every label resolves to its canonical -// encoding type. -const encodings = new Map<string, string>(); -for (const key of Object.keys(encodingMap)) { - const labels = encodingMap[key]; - for (const label of labels) { - encodings.set(label, key); - } -} - -// A map of functions that return new instances of a decoder indexed by the -// encoding type. -const decoders = new Map<string, (options: DecoderOptions) => Decoder>(); -decoders.set( - "utf-8", - (options: DecoderOptions): UTF8Decoder => { - return new UTF8Decoder(options); - } -); - -// Single byte decoders are an array of code point lookups -const encodingIndexes = new Map<string, number[]>(); -// prettier-ignore -encodingIndexes.set("windows-1252", [8364,129,8218,402,8222,8230,8224,8225,710,8240,352,8249,338,141,381,143,144,8216,8217,8220,8221,8226,8211,8212,732,8482,353,8250,339,157,382,376,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255]); -for (const [key, index] of encodingIndexes) { - decoders.set( - key, - (options: DecoderOptions): SingleByteDecoder => { - return new SingleByteDecoder(index, options); - } - ); -} - -function codePointsToString(codePoints: number[]): string { - let s = ""; - for (const cp of codePoints) { - s += String.fromCodePoint(cp); - } - return s; -} - -class Stream { - private _tokens: number[]; - constructor(tokens: number[] | Uint8Array) { - this._tokens = [].slice.call(tokens); - this._tokens.reverse(); - } - - endOfStream(): boolean { - return !this._tokens.length; - } - - read(): number { - return !this._tokens.length ? END_OF_STREAM : this._tokens.pop()!; - } - - prepend(token: number | number[]): void { - if (Array.isArray(token)) { - while (token.length) { - this._tokens.push(token.pop()!); - } - } else { - this._tokens.push(token); - } - } - - push(token: number | number[]): void { - if (Array.isArray(token)) { - while (token.length) { - this._tokens.unshift(token.shift()!); - } - } else { - this._tokens.unshift(token); - } - } -} - -export interface TextDecodeOptions { - stream?: false; -} - -export interface TextDecoderOptions { - fatal?: boolean; - ignoreBOM?: boolean; -} - -type EitherArrayBuffer = SharedArrayBuffer | ArrayBuffer; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function isEitherArrayBuffer(x: any): x is EitherArrayBuffer { - return x instanceof SharedArrayBuffer || x instanceof ArrayBuffer; -} - -export class TextDecoder { - private _encoding: string; - - /** Returns encoding's name, lowercased. */ - get encoding(): string { - return this._encoding; - } - /** Returns `true` if error mode is "fatal", and `false` otherwise. */ - readonly fatal: boolean = false; - /** Returns `true` if ignore BOM flag is set, and `false` otherwise. */ - readonly ignoreBOM: boolean = false; - - constructor(label = "utf-8", options: TextDecoderOptions = { fatal: false }) { - if (options.ignoreBOM) { - this.ignoreBOM = true; - } - if (options.fatal) { - this.fatal = true; - } - label = String(label) - .trim() - .toLowerCase(); - const encoding = encodings.get(label); - if (!encoding) { - throw new RangeError( - `The encoding label provided ('${label}') is invalid.` - ); - } - if (!decoders.has(encoding)) { - throw new TypeError(`Internal decoder ('${encoding}') not found.`); - } - this._encoding = encoding; - } - - /** Returns the result of running encoding's decoder. */ - decode( - input?: domTypes.BufferSource, - options: TextDecodeOptions = { stream: false } - ): string { - if (options.stream) { - throw new TypeError("Stream not supported."); - } - - let bytes: Uint8Array; - if (input instanceof Uint8Array) { - bytes = input; - } else if (isEitherArrayBuffer(input)) { - bytes = new Uint8Array(input); - } else if ( - typeof input === "object" && - "buffer" in input && - isEitherArrayBuffer(input.buffer) - ) { - bytes = new Uint8Array(input.buffer, input.byteOffset, input.byteLength); - } else { - bytes = new Uint8Array(0); - } - - const decoder = decoders.get(this._encoding)!({ - fatal: this.fatal, - ignoreBOM: this.ignoreBOM - }); - const inputStream = new Stream(bytes); - const output: number[] = []; - - while (true) { - const result = decoder.handler(inputStream, inputStream.read()); - if (result === FINISHED) { - break; - } - - if (result !== CONTINUE) { - output.push(result); - } - } - - if (output.length > 0 && output[0] === 0xfeff) { - output.shift(); - } - - return codePointsToString(output); - } - get [Symbol.toStringTag](): string { - return "TextDecoder"; - } -} - -interface TextEncoderEncodeIntoResult { - read: number; - written: number; -} - -export class TextEncoder { - /** Returns "utf-8". */ - readonly encoding = "utf-8"; - /** Returns the result of running UTF-8's encoder. */ - encode(input = ""): Uint8Array { - const encoder = new UTF8Encoder(); - const inputStream = new Stream(stringToCodePoints(input)); - const output: number[] = []; - - while (true) { - const result = encoder.handler(inputStream.read()); - if (result === FINISHED) { - break; - } - if (Array.isArray(result)) { - output.push(...result); - } else { - output.push(result); - } - } - - return new Uint8Array(output); - } - encodeInto(input: string, dest: Uint8Array): TextEncoderEncodeIntoResult { - const encoder = new UTF8Encoder(); - const inputStream = new Stream(stringToCodePoints(input)); - - let written = 0; - let read = 0; - while (true) { - const result = encoder.handler(inputStream.read()); - if (result === FINISHED) { - break; - } - read++; - if (Array.isArray(result)) { - dest.set(result, written); - written += result.length; - if (result.length > 3) { - // increment read a second time if greater than U+FFFF - read++; - } - } else { - dest[written] = result; - written++; - } - } - - return { - read, - written - }; - } - get [Symbol.toStringTag](): string { - return "TextEncoder"; - } -} diff --git a/js/text_encoding_test.ts b/js/text_encoding_test.ts deleted file mode 100644 index aaa9e6b9d..000000000 --- a/js/text_encoding_test.ts +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, assert, assertEquals } from "./test_util.ts"; - -test(function btoaSuccess(): void { - const text = "hello world"; - const encoded = btoa(text); - assertEquals(encoded, "aGVsbG8gd29ybGQ="); -}); - -test(function atobSuccess(): void { - const encoded = "aGVsbG8gd29ybGQ="; - const decoded = atob(encoded); - assertEquals(decoded, "hello world"); -}); - -test(function atobWithAsciiWhitespace(): void { - const encodedList = [ - " aGVsbG8gd29ybGQ=", - " aGVsbG8gd29ybGQ=", - "aGVsbG8gd29ybGQ= ", - "aGVsbG8gd29ybGQ=\n", - "aGVsbG\t8gd29ybGQ=", - `aGVsbG\t8g - d29ybGQ=` - ]; - - for (const encoded of encodedList) { - const decoded = atob(encoded); - assertEquals(decoded, "hello world"); - } -}); - -test(function atobThrows(): void { - let threw = false; - try { - atob("aGVsbG8gd29ybGQ=="); - } catch (e) { - threw = true; - } - assert(threw); -}); - -test(function atobThrows2(): void { - let threw = false; - try { - atob("aGVsbG8gd29ybGQ==="); - } catch (e) { - threw = true; - } - assert(threw); -}); - -test(function btoaFailed(): void { - const text = "你好"; - let err; - try { - btoa(text); - } catch (e) { - err = e; - } - assert(!!err); - assertEquals(err.name, "InvalidInput"); -}); - -test(function textDecoder2(): void { - // prettier-ignore - const fixture = new Uint8Array([ - 0xf0, 0x9d, 0x93, 0xbd, - 0xf0, 0x9d, 0x93, 0xae, - 0xf0, 0x9d, 0x94, 0x81, - 0xf0, 0x9d, 0x93, 0xbd - ]); - const decoder = new TextDecoder(); - assertEquals(decoder.decode(fixture), "𝓽𝓮𝔁𝓽"); -}); - -test(function textDecoderIgnoreBOM(): void { - // prettier-ignore - const fixture = new Uint8Array([ - 0xef, 0xbb, 0xbf, - 0xf0, 0x9d, 0x93, 0xbd, - 0xf0, 0x9d, 0x93, 0xae, - 0xf0, 0x9d, 0x94, 0x81, - 0xf0, 0x9d, 0x93, 0xbd - ]); - const decoder = new TextDecoder("utf-8", { ignoreBOM: true }); - assertEquals(decoder.decode(fixture), "𝓽𝓮𝔁𝓽"); -}); - -test(function textDecoderNotBOM(): void { - // prettier-ignore - const fixture = new Uint8Array([ - 0xef, 0xbb, 0x89, - 0xf0, 0x9d, 0x93, 0xbd, - 0xf0, 0x9d, 0x93, 0xae, - 0xf0, 0x9d, 0x94, 0x81, - 0xf0, 0x9d, 0x93, 0xbd - ]); - const decoder = new TextDecoder("utf-8", { ignoreBOM: true }); - assertEquals(decoder.decode(fixture), "ﻉ𝓽𝓮𝔁𝓽"); -}); - -test(function textDecoderASCII(): void { - const fixture = new Uint8Array([0x89, 0x95, 0x9f, 0xbf]); - const decoder = new TextDecoder("ascii"); - assertEquals(decoder.decode(fixture), "‰•Ÿ¿"); -}); - -test(function textDecoderErrorEncoding(): void { - let didThrow = false; - try { - new TextDecoder("foo"); - } catch (e) { - didThrow = true; - assertEquals(e.message, "The encoding label provided ('foo') is invalid."); - } - assert(didThrow); -}); - -test(function textEncoder(): void { - const fixture = "𝓽𝓮𝔁𝓽"; - const encoder = new TextEncoder(); - // prettier-ignore - assertEquals(Array.from(encoder.encode(fixture)), [ - 0xf0, 0x9d, 0x93, 0xbd, - 0xf0, 0x9d, 0x93, 0xae, - 0xf0, 0x9d, 0x94, 0x81, - 0xf0, 0x9d, 0x93, 0xbd - ]); -}); - -test(function textEncodeInto(): void { - const fixture = "text"; - const encoder = new TextEncoder(); - const bytes = new Uint8Array(5); - const result = encoder.encodeInto(fixture, bytes); - assertEquals(result.read, 4); - assertEquals(result.written, 4); - // prettier-ignore - assertEquals(Array.from(bytes), [ - 0x74, 0x65, 0x78, 0x74, 0x00, - ]); -}); - -test(function textEncodeInto2(): void { - const fixture = "𝓽𝓮𝔁𝓽"; - const encoder = new TextEncoder(); - const bytes = new Uint8Array(17); - const result = encoder.encodeInto(fixture, bytes); - assertEquals(result.read, 8); - assertEquals(result.written, 16); - // prettier-ignore - assertEquals(Array.from(bytes), [ - 0xf0, 0x9d, 0x93, 0xbd, - 0xf0, 0x9d, 0x93, 0xae, - 0xf0, 0x9d, 0x94, 0x81, - 0xf0, 0x9d, 0x93, 0xbd, 0x00, - ]); -}); - -test(function textDecoderSharedUint8Array(): void { - const ab = new SharedArrayBuffer(6); - const dataView = new DataView(ab); - const charCodeA = "A".charCodeAt(0); - for (let i = 0; i < ab.byteLength; i++) { - dataView.setUint8(i, charCodeA + i); - } - const ui8 = new Uint8Array(ab); - const decoder = new TextDecoder(); - const actual = decoder.decode(ui8); - assertEquals(actual, "ABCDEF"); -}); - -test(function textDecoderSharedInt32Array(): void { - const ab = new SharedArrayBuffer(8); - const dataView = new DataView(ab); - const charCodeA = "A".charCodeAt(0); - for (let i = 0; i < ab.byteLength; i++) { - dataView.setUint8(i, charCodeA + i); - } - const i32 = new Int32Array(ab); - const decoder = new TextDecoder(); - const actual = decoder.decode(i32); - assertEquals(actual, "ABCDEFGH"); -}); - -test(function toStringShouldBeWebCompatibility(): void { - const encoder = new TextEncoder(); - assertEquals(encoder.toString(), "[object TextEncoder]"); - - const decoder = new TextDecoder(); - assertEquals(decoder.toString(), "[object TextDecoder]"); -}); diff --git a/js/timers.ts b/js/timers.ts deleted file mode 100644 index 5bc4922e3..000000000 --- a/js/timers.ts +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { assert } from "./util.ts"; -import { window } from "./window.ts"; -import * as dispatch from "./dispatch.ts"; -import { sendSync, sendAsync } from "./dispatch_json.ts"; - -const { console } = window; - -interface Timer { - id: number; - callback: () => void; - delay: number; - due: number; - repeat: boolean; - scheduled: boolean; -} - -// We'll subtract EPOCH every time we retrieve the time with Date.now(). This -// ensures that absolute time values stay below UINT32_MAX - 2, which is the -// maximum object key that EcmaScript considers "numerical". After running for -// about a month, this is no longer true, and Deno explodes. -// TODO(piscisaureus): fix that ^. -const EPOCH = Date.now(); -const APOCALYPSE = 2 ** 32 - 2; - -// Timeout values > TIMEOUT_MAX are set to 1. -const TIMEOUT_MAX = 2 ** 31 - 1; - -let globalTimeoutDue: number | null = null; - -let nextTimerId = 1; -const idMap = new Map<number, Timer>(); -const dueMap: { [due: number]: Timer[] } = Object.create(null); - -function getTime(): number { - // TODO: use a monotonic clock. - const now = Date.now() - EPOCH; - assert(now >= 0 && now < APOCALYPSE); - return now; -} - -function clearGlobalTimeout(): void { - globalTimeoutDue = null; - sendSync(dispatch.OP_GLOBAL_TIMER_STOP); -} - -async function setGlobalTimeout(due: number, now: number): Promise<void> { - // Since JS and Rust don't use the same clock, pass the time to rust as a - // relative time value. On the Rust side we'll turn that into an absolute - // value again. - const timeout = due - now; - assert(timeout >= 0); - - // Send message to the backend. - globalTimeoutDue = due; - await sendAsync(dispatch.OP_GLOBAL_TIMER, { timeout }); - // eslint-disable-next-line @typescript-eslint/no-use-before-define - fireTimers(); -} - -function setOrClearGlobalTimeout(due: number | null, now: number): void { - if (due == null) { - clearGlobalTimeout(); - } else { - setGlobalTimeout(due, now); - } -} - -function schedule(timer: Timer, now: number): void { - assert(!timer.scheduled); - assert(now <= timer.due); - // Find or create the list of timers that will fire at point-in-time `due`. - let list = dueMap[timer.due]; - if (list === undefined) { - list = dueMap[timer.due] = []; - } - // Append the newly scheduled timer to the list and mark it as scheduled. - list.push(timer); - timer.scheduled = true; - // If the new timer is scheduled to fire before any timer that existed before, - // update the global timeout to reflect this. - if (globalTimeoutDue === null || globalTimeoutDue > timer.due) { - setOrClearGlobalTimeout(timer.due, now); - } -} - -function unschedule(timer: Timer): void { - if (!timer.scheduled) { - return; - } - // Find the list of timers that will fire at point-in-time `due`. - const list = dueMap[timer.due]; - if (list.length === 1) { - // Time timer is the only one in the list. Remove the entire list. - assert(list[0] === timer); - delete dueMap[timer.due]; - // If the unscheduled timer was 'next up', find when the next timer that - // still exists is due, and update the global alarm accordingly. - if (timer.due === globalTimeoutDue) { - let nextTimerDue: number | null = null; - for (const key in dueMap) { - nextTimerDue = Number(key); - break; - } - setOrClearGlobalTimeout(nextTimerDue, getTime()); - } - } else { - // Multiple timers that are due at the same point in time. - // Remove this timer from the list. - const index = list.indexOf(timer); - assert(index > -1); - list.splice(index, 1); - } -} - -function fire(timer: Timer): void { - // If the timer isn't found in the ID map, that means it has been cancelled - // between the timer firing and the promise callback (this function). - if (!idMap.has(timer.id)) { - return; - } - // Reschedule the timer if it is a repeating one, otherwise drop it. - if (!timer.repeat) { - // One-shot timer: remove the timer from this id-to-timer map. - idMap.delete(timer.id); - } else { - // Interval timer: compute when timer was supposed to fire next. - // However make sure to never schedule the next interval in the past. - const now = getTime(); - timer.due = Math.max(now, timer.due + timer.delay); - schedule(timer, now); - } - // Call the user callback. Intermediate assignment is to avoid leaking `this` - // to it, while also keeping the stack trace neat when it shows up in there. - const callback = timer.callback; - callback(); -} - -function fireTimers(): void { - const now = getTime(); - // Bail out if we're not expecting the global timer to fire. - if (globalTimeoutDue === null) { - return; - } - // After firing the timers that are due now, this will hold the due time of - // the first timer that hasn't fired yet. - let nextTimerDue: number | null = null; - // Walk over the keys of the 'due' map. Since dueMap is actually a regular - // object and its keys are numerical and smaller than UINT32_MAX - 2, - // keys are iterated in ascending order. - for (const key in dueMap) { - // Convert the object key (a string) to a number. - const due = Number(key); - // Break out of the loop if the next timer isn't due to fire yet. - if (Number(due) > now) { - nextTimerDue = due; - break; - } - // Get the list of timers that have this due time, then drop it. - const list = dueMap[key]; - delete dueMap[key]; - // Fire all the timers in the list. - for (const timer of list) { - // With the list dropped, the timer is no longer scheduled. - timer.scheduled = false; - // Place the callback on the microtask queue. - Promise.resolve(timer).then(fire); - } - } - - // Update the global alarm to go off when the first-up timer that hasn't fired - // yet is due. - setOrClearGlobalTimeout(nextTimerDue, now); -} - -export type Args = unknown[]; - -function checkThis(thisArg: unknown): void { - if (thisArg !== null && thisArg !== undefined && thisArg !== window) { - throw new TypeError("Illegal invocation"); - } -} - -function checkBigInt(n: unknown): void { - if (typeof n === "bigint") { - throw new TypeError("Cannot convert a BigInt value to a number"); - } -} - -function setTimer( - cb: (...args: Args) => void, - delay: number, - args: Args, - repeat: boolean -): number { - // Bind `args` to the callback and bind `this` to window(global). - const callback: () => void = cb.bind(window, ...args); - // In the browser, the delay value must be coercible to an integer between 0 - // and INT32_MAX. Any other value will cause the timer to fire immediately. - // We emulate this behavior. - const now = getTime(); - if (delay > TIMEOUT_MAX) { - console.warn( - `${delay} does not fit into` + - " a 32-bit signed integer." + - "\nTimeout duration was set to 1." - ); - delay = 1; - } - delay = Math.max(0, delay | 0); - - // Create a new, unscheduled timer object. - const timer = { - id: nextTimerId++, - callback, - args, - delay, - due: now + delay, - repeat, - scheduled: false - }; - // Register the timer's existence in the id-to-timer map. - idMap.set(timer.id, timer); - // Schedule the timer in the due table. - schedule(timer, now); - return timer.id; -} - -/** Sets a timer which executes a function once after the timer expires. */ -export function setTimeout( - cb: (...args: Args) => void, - delay = 0, - ...args: Args -): number { - checkBigInt(delay); - // @ts-ignore - checkThis(this); - return setTimer(cb, delay, args, false); -} - -/** Repeatedly calls a function , with a fixed time delay between each call. */ -export function setInterval( - cb: (...args: Args) => void, - delay = 0, - ...args: Args -): number { - checkBigInt(delay); - // @ts-ignore - checkThis(this); - return setTimer(cb, delay, args, true); -} - -/** Clears a previously set timer by id. AKA clearTimeout and clearInterval. */ -function clearTimer(id: number): void { - id = Number(id); - const timer = idMap.get(id); - if (timer === undefined) { - // Timer doesn't exist any more or never existed. This is not an error. - return; - } - // Unschedule the timer if it is currently scheduled, and forget about it. - unschedule(timer); - idMap.delete(timer.id); -} - -export function clearTimeout(id = 0): void { - checkBigInt(id); - if (id === 0) { - return; - } - clearTimer(id); -} - -export function clearInterval(id = 0): void { - checkBigInt(id); - if (id === 0) { - return; - } - clearTimer(id); -} diff --git a/js/timers_test.ts b/js/timers_test.ts deleted file mode 100644 index bc4fcffcf..000000000 --- a/js/timers_test.ts +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, assert, assertEquals, assertNotEquals } from "./test_util.ts"; - -function deferred(): { - promise: Promise<{}>; - resolve: (value?: {} | PromiseLike<{}>) => void; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - reject: (reason?: any) => void; -} { - let resolve; - let reject; - const promise = new Promise( - (res, rej): void => { - resolve = res; - reject = rej; - } - ); - return { - promise, - resolve, - reject - }; -} - -async function waitForMs(ms): Promise<number> { - return new Promise((resolve): number => setTimeout(resolve, ms)); -} - -test(async function timeoutSuccess(): Promise<void> { - const { promise, resolve } = deferred(); - let count = 0; - setTimeout((): void => { - count++; - resolve(); - }, 500); - await promise; - // count should increment - assertEquals(count, 1); -}); - -test(async function timeoutArgs(): Promise<void> { - const { promise, resolve } = deferred(); - const arg = 1; - setTimeout( - (a, b, c): void => { - assertEquals(a, arg); - assertEquals(b, arg.toString()); - assertEquals(c, [arg]); - resolve(); - }, - 10, - arg, - arg.toString(), - [arg] - ); - await promise; -}); - -test(async function timeoutCancelSuccess(): Promise<void> { - let count = 0; - const id = setTimeout((): void => { - count++; - }, 1); - // Cancelled, count should not increment - clearTimeout(id); - await waitForMs(600); - assertEquals(count, 0); -}); - -test(async function timeoutCancelMultiple(): Promise<void> { - function uncalled(): never { - throw new Error("This function should not be called."); - } - - // Set timers and cancel them in the same order. - const t1 = setTimeout(uncalled, 10); - const t2 = setTimeout(uncalled, 10); - const t3 = setTimeout(uncalled, 10); - clearTimeout(t1); - clearTimeout(t2); - clearTimeout(t3); - - // Set timers and cancel them in reverse order. - const t4 = setTimeout(uncalled, 20); - const t5 = setTimeout(uncalled, 20); - const t6 = setTimeout(uncalled, 20); - clearTimeout(t6); - clearTimeout(t5); - clearTimeout(t4); - - // Sleep until we're certain that the cancelled timers aren't gonna fire. - await waitForMs(50); -}); - -test(async function timeoutCancelInvalidSilentFail(): Promise<void> { - // Expect no panic - const { promise, resolve } = deferred(); - let count = 0; - const id = setTimeout((): void => { - count++; - // Should have no effect - clearTimeout(id); - resolve(); - }, 500); - await promise; - assertEquals(count, 1); - - // Should silently fail (no panic) - clearTimeout(2147483647); -}); - -test(async function intervalSuccess(): Promise<void> { - const { promise, resolve } = deferred(); - let count = 0; - const id = setInterval((): void => { - count++; - clearInterval(id); - resolve(); - }, 100); - await promise; - // Clear interval - clearInterval(id); - // count should increment twice - assertEquals(count, 1); -}); - -test(async function intervalCancelSuccess(): Promise<void> { - let count = 0; - const id = setInterval((): void => { - count++; - }, 1); - clearInterval(id); - await waitForMs(500); - assertEquals(count, 0); -}); - -test(async function intervalOrdering(): Promise<void> { - const timers = []; - let timeouts = 0; - function onTimeout(): void { - ++timeouts; - for (let i = 1; i < timers.length; i++) { - clearTimeout(timers[i]); - } - } - for (let i = 0; i < 10; i++) { - timers[i] = setTimeout(onTimeout, 1); - } - await waitForMs(500); - assertEquals(timeouts, 1); -}); - -test(async function intervalCancelInvalidSilentFail(): Promise<void> { - // Should silently fail (no panic) - clearInterval(2147483647); -}); - -test(async function fireCallbackImmediatelyWhenDelayOverMaxValue(): Promise< - void -> { - let count = 0; - setTimeout((): void => { - count++; - }, 2 ** 31); - await waitForMs(1); - assertEquals(count, 1); -}); - -test(async function timeoutCallbackThis(): Promise<void> { - const { promise, resolve } = deferred(); - const obj = { - foo(): void { - assertEquals(this, window); - resolve(); - } - }; - setTimeout(obj.foo, 1); - await promise; -}); - -test(async function timeoutBindThis(): Promise<void> { - function noop(): void {} - - const thisCheckPassed = [null, undefined, window, globalThis]; - - const thisCheckFailed = [ - 0, - "", - true, - false, - {}, - [], - "foo", - (): void => {}, - Object.prototype - ]; - - thisCheckPassed.forEach( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (thisArg: any): void => { - let hasThrown = 0; - try { - setTimeout.call(thisArg, noop, 1); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 1); - } - ); - - thisCheckFailed.forEach( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (thisArg: any): void => { - let hasThrown = 0; - try { - setTimeout.call(thisArg, noop, 1); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); - } - ); -}); - -test(async function clearTimeoutShouldConvertToNumber(): Promise<void> { - let called = false; - const obj = { - valueOf(): number { - called = true; - return 1; - } - }; - clearTimeout((obj as unknown) as number); - assert(called); -}); - -test(function setTimeoutShouldThrowWithBigint(): void { - let hasThrown = 0; - try { - setTimeout((): void => {}, (1n as unknown) as number); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); -}); - -test(function clearTimeoutShouldThrowWithBigint(): void { - let hasThrown = 0; - try { - clearTimeout((1n as unknown) as number); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); -}); - -test(function testFunctionName(): void { - assertEquals(clearTimeout.name, "clearTimeout"); - assertEquals(clearInterval.name, "clearInterval"); -}); - -test(function testFunctionParamsLength(): void { - assertEquals(setTimeout.length, 1); - assertEquals(setInterval.length, 1); - assertEquals(clearTimeout.length, 0); - assertEquals(clearInterval.length, 0); -}); - -test(function clearTimeoutAndClearIntervalNotBeEquals(): void { - assertNotEquals(clearTimeout, clearInterval); -}); diff --git a/js/tls.ts b/js/tls.ts deleted file mode 100644 index ec24b458b..000000000 --- a/js/tls.ts +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendAsync } from "./dispatch_json.ts"; -import * as dispatch from "./dispatch.ts"; -import { Conn, ConnImpl } from "./net.ts"; - -// TODO(ry) There are many configuration options to add... -// https://docs.rs/rustls/0.16.0/rustls/struct.ClientConfig.html -interface DialTLSOptions { - port: number; - hostname?: string; -} -const dialTLSDefaults = { hostname: "127.0.0.1", transport: "tcp" }; - -/** - * dialTLS establishes a secure connection over TLS (transport layer security). - */ -export async function dialTLS(options: DialTLSOptions): Promise<Conn> { - options = Object.assign(dialTLSDefaults, options); - const res = await sendAsync(dispatch.OP_DIAL_TLS, options); - return new ConnImpl(res.rid, res.remoteAddr!, res.localAddr!); -} diff --git a/js/tls_test.ts b/js/tls_test.ts deleted file mode 100644 index 25900f876..000000000 --- a/js/tls_test.ts +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, testPerm, assert, assertEquals } from "./test_util.ts"; - -// TODO(ry) The tests in this file use github.com:443, but it would be better to -// not rely on an internet connection and rather use a localhost TLS server. - -test(async function dialTLSNoPerm(): Promise<void> { - let err; - try { - await Deno.dialTLS({ hostname: "github.com", port: 443 }); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -testPerm({ net: true }, async function dialTLSBasic(): Promise<void> { - const conn = await Deno.dialTLS({ hostname: "github.com", port: 443 }); - assert(conn.rid > 0); - const body = new TextEncoder().encode("GET / HTTP/1.0\r\n\r\n"); - const writeResult = await conn.write(body); - assertEquals(body.length, writeResult); - conn.close(); -}); diff --git a/js/truncate.ts b/js/truncate.ts deleted file mode 100644 index 5ce7b5158..000000000 --- a/js/truncate.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendSync, sendAsync } from "./dispatch_json.ts"; -import * as dispatch from "./dispatch.ts"; - -function coerceLen(len?: number): number { - if (!len) { - return 0; - } - - if (len < 0) { - return 0; - } - - return len; -} - -/** Truncates or extends the specified file synchronously, updating the size of - * this file to become size. - * - * Deno.truncateSync("hello.txt", 10); - */ -export function truncateSync(name: string, len?: number): void { - sendSync(dispatch.OP_TRUNCATE, { name, len: coerceLen(len) }); -} - -/** - * Truncates or extends the specified file, updating the size of this file to - * become size. - * - * await Deno.truncate("hello.txt", 10); - */ -export async function truncate(name: string, len?: number): Promise<void> { - await sendAsync(dispatch.OP_TRUNCATE, { name, len: coerceLen(len) }); -} diff --git a/js/truncate_test.ts b/js/truncate_test.ts deleted file mode 100644 index 055db8652..000000000 --- a/js/truncate_test.ts +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { testPerm, assertEquals } from "./test_util.ts"; - -function readDataSync(name: string): string { - const data = Deno.readFileSync(name); - const decoder = new TextDecoder("utf-8"); - const text = decoder.decode(data); - return text; -} - -async function readData(name: string): Promise<string> { - const data = await Deno.readFile(name); - const decoder = new TextDecoder("utf-8"); - const text = decoder.decode(data); - return text; -} - -testPerm({ read: true, write: true }, function truncateSyncSuccess(): void { - const enc = new TextEncoder(); - const d = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test_truncateSync.txt"; - Deno.writeFileSync(filename, d); - Deno.truncateSync(filename, 20); - let data = readDataSync(filename); - assertEquals(data.length, 20); - Deno.truncateSync(filename, 5); - data = readDataSync(filename); - assertEquals(data.length, 5); - Deno.truncateSync(filename, -5); - data = readDataSync(filename); - assertEquals(data.length, 0); - Deno.removeSync(filename); -}); - -testPerm({ read: true, write: true }, async function truncateSuccess(): Promise< - void -> { - const enc = new TextEncoder(); - const d = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test_truncate.txt"; - await Deno.writeFile(filename, d); - await Deno.truncate(filename, 20); - let data = await readData(filename); - assertEquals(data.length, 20); - await Deno.truncate(filename, 5); - data = await readData(filename); - assertEquals(data.length, 5); - await Deno.truncate(filename, -5); - data = await readData(filename); - assertEquals(data.length, 0); - await Deno.remove(filename); -}); - -testPerm({ write: false }, function truncateSyncPerm(): void { - let err; - try { - Deno.mkdirSync("/test_truncateSyncPermission.txt"); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); - -testPerm({ write: false }, async function truncatePerm(): Promise<void> { - let err; - try { - await Deno.mkdir("/test_truncatePermission.txt"); - } catch (e) { - err = e; - } - assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(err.name, "PermissionDenied"); -}); diff --git a/js/ts_global.d.ts b/js/ts_global.d.ts deleted file mode 100644 index 71a01e30e..000000000 --- a/js/ts_global.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. - -// This scopes the `ts` namespace globally, which is where it exists at runtime -// when building Deno, but the `typescript/lib/typescript.d.ts` is defined as a -// module. - -// Warning! This is a magical import. We don't want to have multiple copies of -// typescript.d.ts around the repo, there's already one in -// deno_typescript/typescript/lib/typescript.d.ts. Ideally we could simply point -// to that in this import specifier, but "cargo package" is very strict and -// requires all files to be present in a crate's subtree. -// eslint-disable-next-line @typescript-eslint/no-unused-vars -import * as ts_ from "$asset$/typescript.d.ts"; - -declare global { - namespace ts { - export = ts_; - } -} diff --git a/js/type_directives.ts b/js/type_directives.ts deleted file mode 100644 index 9b27887b5..000000000 --- a/js/type_directives.ts +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. - -interface FileReference { - fileName: string; - pos: number; - end: number; -} - -/** Remap the module name based on any supplied type directives passed. */ -export function getMappedModuleName( - source: FileReference, - typeDirectives: Map<FileReference, string> -): string { - const { fileName: sourceFileName, pos: sourcePos } = source; - for (const [{ fileName, pos }, value] of typeDirectives.entries()) { - if (sourceFileName === fileName && sourcePos === pos) { - return value; - } - } - return source.fileName; -} - -/** Matches directives that look something like this and parses out the value - * of the directive: - * - * // @deno-types="./foo.d.ts" - * - * [See Diagram](http://bit.ly/31nZPCF) - */ -const typeDirectiveRegEx = /@deno-types\s*=\s*(["'])((?:(?=(\\?))\3.)*?)\1/gi; - -/** Matches `import`, `import from` or `export from` statements and parses out the value of the - * module specifier in the second capture group: - * - * import "./foo.js" - * import * as foo from "./foo.js" - * export { a, b, c } from "./bar.js" - * - * [See Diagram](http://bit.ly/2lOsp0K) - */ -const importExportRegEx = /(?:import|export)(?:\s+|\s+[\s\S]*?from\s+)?(["'])((?:(?=(\\?))\3.)*?)\1/; - -/** Parses out any Deno type directives that are part of the source code, or - * returns `undefined` if there are not any. - */ -export function parseTypeDirectives( - sourceCode: string | undefined -): Map<FileReference, string> | undefined { - if (!sourceCode) { - return; - } - - // collect all the directives in the file and their start and end positions - const directives: FileReference[] = []; - let maybeMatch: RegExpExecArray | null = null; - while ((maybeMatch = typeDirectiveRegEx.exec(sourceCode))) { - const [matchString, , fileName] = maybeMatch; - const { index: pos } = maybeMatch; - directives.push({ - fileName, - pos, - end: pos + matchString.length - }); - } - if (!directives.length) { - return; - } - - // work from the last directive backwards for the next `import`/`export` - // statement - directives.reverse(); - const results = new Map<FileReference, string>(); - for (const { end, fileName, pos } of directives) { - const searchString = sourceCode.substring(end); - const maybeMatch = importExportRegEx.exec(searchString); - if (maybeMatch) { - const [matchString, , targetFileName] = maybeMatch; - const targetPos = - end + maybeMatch.index + matchString.indexOf(targetFileName) - 1; - const target: FileReference = { - fileName: targetFileName, - pos: targetPos, - end: targetPos + targetFileName.length - }; - results.set(target, fileName); - } - sourceCode = sourceCode.substring(0, pos); - } - - return results; -} diff --git a/js/types.ts b/js/types.ts deleted file mode 100644 index 88462d758..000000000 --- a/js/types.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -export type TypedArray = Uint8Array | Float32Array | Int32Array; diff --git a/js/unit_test_runner.ts b/js/unit_test_runner.ts deleted file mode 100755 index 24dae7706..000000000 --- a/js/unit_test_runner.ts +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env -S deno run --reload --allow-run -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import "./unit_tests.ts"; -import { permissionCombinations, parseUnitTestOutput } from "./test_util.ts"; - -interface TestResult { - perms: string; - output: string; - result: number; -} - -function permsToCliFlags(perms: Deno.Permissions): string[] { - return Object.keys(perms) - .map( - (key): string => { - if (!perms[key]) return ""; - - const cliFlag = key.replace( - /\.?([A-Z])/g, - (x, y): string => `-${y.toLowerCase()}` - ); - return `--allow-${cliFlag}`; - } - ) - .filter((e): boolean => e.length > 0); -} - -function fmtPerms(perms: Deno.Permissions): string { - let fmt = permsToCliFlags(perms).join(" "); - - if (!fmt) { - fmt = "<no permissions>"; - } - - return fmt; -} - -async function main(): Promise<void> { - console.log( - "Discovered permission combinations for tests:", - permissionCombinations.size - ); - - for (const perms of permissionCombinations.values()) { - console.log("\t" + fmtPerms(perms)); - } - - const testResults = new Set<TestResult>(); - - for (const perms of permissionCombinations.values()) { - const permsFmt = fmtPerms(perms); - console.log(`Running tests for: ${permsFmt}`); - const cliPerms = permsToCliFlags(perms); - // run subsequent tests using same deno executable - const args = [ - Deno.execPath(), - "run", - "--no-prompt", - ...cliPerms, - "js/unit_tests.ts" - ]; - - const p = Deno.run({ - args, - stdout: "piped" - }); - - const { actual, expected, resultOutput } = parseUnitTestOutput( - await p.output(), - true - ); - - let result = 0; - - if (!actual && !expected) { - console.error("Bad js/unit_test.ts output"); - result = 1; - } else if (expected !== actual) { - result = 1; - } - - testResults.add({ - perms: permsFmt, - output: resultOutput, - result - }); - } - - // if any run tests returned non-zero status then whole test - // run should fail - let testsFailed = false; - - for (const testResult of testResults) { - console.log(`Summary for ${testResult.perms}`); - console.log(testResult.output + "\n"); - testsFailed = testsFailed || Boolean(testResult.result); - } - - if (testsFailed) { - console.error("Unit tests failed"); - Deno.exit(1); - } - - console.log("Unit tests passed"); -} - -main(); diff --git a/js/unit_tests.ts b/js/unit_tests.ts deleted file mode 100644 index a83b57c15..000000000 --- a/js/unit_tests.ts +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. - -// This test is executed as part of tools/test.py -// But it can also be run manually: ./target/debug/deno js/unit_tests.ts - -import "./blob_test.ts"; -import "./body_test.ts"; -import "./buffer_test.ts"; -import "./build_test.ts"; -import "./chmod_test.ts"; -import "./chown_test.ts"; -import "./console_test.ts"; -import "./copy_file_test.ts"; -import "./custom_event_test.ts"; -import "./dir_test.ts"; -import "./dispatch_json_test.ts"; -import "./error_stack_test.ts"; -import "./event_test.ts"; -import "./event_target_test.ts"; -import "./fetch_test.ts"; -import "./file_test.ts"; -import "./files_test.ts"; -import "./form_data_test.ts"; -import "./get_random_values_test.ts"; -import "./globals_test.ts"; -import "./headers_test.ts"; -import "./link_test.ts"; -import "./location_test.ts"; -import "./make_temp_dir_test.ts"; -import "./metrics_test.ts"; -import "./mixins/dom_iterable_test.ts"; -import "./mkdir_test.ts"; -import "./net_test.ts"; -import "./os_test.ts"; -import "./process_test.ts"; -import "./read_dir_test.ts"; -import "./read_file_test.ts"; -import "./read_link_test.ts"; -import "./rename_test.ts"; -import "./request_test.ts"; -import "./resources_test.ts"; -import "./stat_test.ts"; -import "./symlink_test.ts"; -import "./text_encoding_test.ts"; -import "./timers_test.ts"; -import "./tls_test.ts"; -import "./truncate_test.ts"; -import "./url_test.ts"; -import "./url_search_params_test.ts"; -import "./utime_test.ts"; -import "./write_file_test.ts"; -import "./performance_test.ts"; -import "./permissions_test.ts"; -import "./version_test.ts"; - -import "../website/app_test.ts"; - -import { runIfMain } from "../std/testing/mod.ts"; - -async function main(): Promise<void> { - // Testing entire test suite serially - runIfMain(import.meta); -} - -main(); diff --git a/js/url.ts b/js/url.ts deleted file mode 100644 index f22198da4..000000000 --- a/js/url.ts +++ /dev/null @@ -1,376 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import * as urlSearchParams from "./url_search_params.ts"; -import * as domTypes from "./dom_types.ts"; -import { getRandomValues } from "./get_random_values.ts"; -import { window } from "./window.ts"; - -interface URLParts { - protocol: string; - username: string; - password: string; - hostname: string; - port: string; - path: string; - query: string | null; - hash: string; -} - -const patterns = { - protocol: "(?:([^:/?#]+):)", - authority: "(?://([^/?#]*))", - path: "([^?#]*)", - query: "(\\?[^#]*)", - hash: "(#.*)", - - authentication: "(?:([^:]*)(?::([^@]*))?@)", - hostname: "([^:]+)", - port: "(?::(\\d+))" -}; - -const urlRegExp = new RegExp( - `^${patterns.protocol}?${patterns.authority}?${patterns.path}${ - patterns.query - }?${patterns.hash}?` -); - -const authorityRegExp = new RegExp( - `^${patterns.authentication}?${patterns.hostname}${patterns.port}?$` -); - -const searchParamsMethods: Array<keyof urlSearchParams.URLSearchParams> = [ - "append", - "delete", - "set" -]; - -function parse(url: string): URLParts | undefined { - const urlMatch = urlRegExp.exec(url); - if (urlMatch) { - const [, , authority] = urlMatch; - const authorityMatch = authority - ? authorityRegExp.exec(authority) - : [null, null, null, null, null]; - if (authorityMatch) { - return { - protocol: urlMatch[1] || "", - username: authorityMatch[1] || "", - password: authorityMatch[2] || "", - hostname: authorityMatch[3] || "", - port: authorityMatch[4] || "", - path: urlMatch[3] || "", - query: urlMatch[4] || "", - hash: urlMatch[5] || "" - }; - } - } - return undefined; -} - -// Based on https://github.com/kelektiv/node-uuid -// TODO(kevinkassimo): Use deno_std version once possible. -function generateUUID(): string { - return "00000000-0000-4000-8000-000000000000".replace( - /[0]/g, - (): string => - // random integer from 0 to 15 as a hex digit. - (getRandomValues(new Uint8Array(1))[0] % 16).toString(16) - ); -} - -// Keep it outside of URL to avoid any attempts of access. -export const blobURLMap = new Map<string, domTypes.Blob>(); - -function isAbsolutePath(path: string): boolean { - return path.startsWith("/"); -} - -// Resolves `.`s and `..`s where possible. -// Preserves repeating and trailing `/`s by design. -function normalizePath(path: string): string { - const isAbsolute = isAbsolutePath(path); - path = path.replace(/^\//, ""); - const pathSegments = path.split("/"); - - const newPathSegments: string[] = []; - for (let i = 0; i < pathSegments.length; i++) { - const previous = newPathSegments[newPathSegments.length - 1]; - if ( - pathSegments[i] == ".." && - previous != ".." && - (previous != undefined || isAbsolute) - ) { - newPathSegments.pop(); - } else if (pathSegments[i] != ".") { - newPathSegments.push(pathSegments[i]); - } - } - - let newPath = newPathSegments.join("/"); - if (!isAbsolute) { - if (newPathSegments.length == 0) { - newPath = "."; - } - } else { - newPath = `/${newPath}`; - } - return newPath; -} - -// Standard URL basing logic, applied to paths. -function resolvePathFromBase(path: string, basePath: string): string { - const normalizedPath = normalizePath(path); - if (isAbsolutePath(normalizedPath)) { - return normalizedPath; - } - const normalizedBasePath = normalizePath(basePath); - if (!isAbsolutePath(normalizedBasePath)) { - throw new TypeError("Base path must be absolute."); - } - - // Special case. - if (path == "") { - return normalizedBasePath; - } - - // Remove everything after the last `/` in `normalizedBasePath`. - const prefix = normalizedBasePath.replace(/[^\/]*$/, ""); - // If `normalizedPath` ends with `.` or `..`, add a trailing space. - const suffix = normalizedPath.replace(/(?<=(^|\/)(\.|\.\.))$/, "/"); - - return normalizePath(prefix + suffix); -} - -export class URL { - private _parts: URLParts; - private _searchParams!: urlSearchParams.URLSearchParams; - - private _updateSearchParams(): void { - const searchParams = new urlSearchParams.URLSearchParams(this.search); - - for (const methodName of searchParamsMethods) { - /* eslint-disable @typescript-eslint/no-explicit-any */ - const method: (...args: any[]) => any = searchParams[methodName]; - searchParams[methodName] = (...args: unknown[]): any => { - method.apply(searchParams, args); - this.search = searchParams.toString(); - }; - /* eslint-enable */ - } - this._searchParams = searchParams; - - // convert to `any` that has avoided the private limit - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (this._searchParams as any).url = this; - } - - get hash(): string { - return this._parts.hash; - } - - set hash(value: string) { - value = unescape(String(value)); - if (!value) { - this._parts.hash = ""; - } else { - if (value.charAt(0) !== "#") { - value = `#${value}`; - } - // hashes can contain % and # unescaped - this._parts.hash = escape(value) - .replace(/%25/g, "%") - .replace(/%23/g, "#"); - } - } - - get host(): string { - return `${this.hostname}${this.port ? `:${this.port}` : ""}`; - } - - set host(value: string) { - value = String(value); - const url = new URL(`http://${value}`); - this._parts.hostname = url.hostname; - this._parts.port = url.port; - } - - get hostname(): string { - return this._parts.hostname; - } - - set hostname(value: string) { - value = String(value); - this._parts.hostname = encodeURIComponent(value); - } - - get href(): string { - const authentication = - this.username || this.password - ? `${this.username}${this.password ? ":" + this.password : ""}@` - : ""; - - return `${this.protocol}//${authentication}${this.host}${this.pathname}${ - this.search - }${this.hash}`; - } - - set href(value: string) { - value = String(value); - if (value !== this.href) { - const url = new URL(value); - this._parts = { ...url._parts }; - this._updateSearchParams(); - } - } - - get origin(): string { - return `${this.protocol}//${this.host}`; - } - - get password(): string { - return this._parts.password; - } - - set password(value: string) { - value = String(value); - this._parts.password = encodeURIComponent(value); - } - - get pathname(): string { - return this._parts.path ? this._parts.path : "/"; - } - - set pathname(value: string) { - value = unescape(String(value)); - if (!value || value.charAt(0) !== "/") { - value = `/${value}`; - } - // paths can contain % unescaped - this._parts.path = escape(value).replace(/%25/g, "%"); - } - - get port(): string { - return this._parts.port; - } - - set port(value: string) { - const port = parseInt(String(value), 10); - this._parts.port = isNaN(port) - ? "" - : Math.max(0, port % 2 ** 16).toString(); - } - - get protocol(): string { - return `${this._parts.protocol}:`; - } - - set protocol(value: string) { - value = String(value); - if (value) { - if (value.charAt(value.length - 1) === ":") { - value = value.slice(0, -1); - } - this._parts.protocol = encodeURIComponent(value); - } - } - - get search(): string { - if (this._parts.query === null || this._parts.query === "") { - return ""; - } - - return this._parts.query; - } - - set search(value: string) { - value = String(value); - let query: string | null; - - if (value === "") { - query = null; - } else if (value.charAt(0) !== "?") { - query = `?${value}`; - } else { - query = value; - } - - this._parts.query = query; - this._updateSearchParams(); - } - - get username(): string { - return this._parts.username; - } - - set username(value: string) { - value = String(value); - this._parts.username = encodeURIComponent(value); - } - - get searchParams(): urlSearchParams.URLSearchParams { - return this._searchParams; - } - - constructor(url: string, base?: string | URL) { - let baseParts: URLParts | undefined; - if (base) { - baseParts = typeof base === "string" ? parse(base) : base._parts; - if (!baseParts || baseParts.protocol == "") { - throw new TypeError("Invalid base URL."); - } - } - - const urlParts = parse(url); - if (!urlParts) { - throw new TypeError("Invalid URL."); - } - - if (urlParts.protocol) { - this._parts = urlParts; - } else if (baseParts) { - this._parts = { - protocol: baseParts.protocol, - username: baseParts.username, - password: baseParts.password, - hostname: baseParts.hostname, - port: baseParts.port, - path: resolvePathFromBase(urlParts.path, baseParts.path || "/"), - query: urlParts.query, - hash: urlParts.hash - }; - } else { - throw new TypeError("URL requires a base URL."); - } - this._updateSearchParams(); - } - - toString(): string { - return this.href; - } - - toJSON(): string { - return this.href; - } - - // TODO(kevinkassimo): implement MediaSource version in the future. - static createObjectURL(b: domTypes.Blob): string { - const origin = window.location.origin || "http://deno-opaque-origin"; - const key = `blob:${origin}/${generateUUID()}`; - blobURLMap.set(key, b); - return key; - } - - static revokeObjectURL(url: string): void { - let urlObject; - try { - urlObject = new URL(url); - } catch { - throw new TypeError("Provided URL string is not valid"); - } - if (urlObject.protocol !== "blob:") { - return; - } - // Origin match check seems irrelevant for now, unless we implement - // persisten storage for per window.location.origin at some point. - blobURLMap.delete(url); - } -} diff --git a/js/url_search_params.ts b/js/url_search_params.ts deleted file mode 100644 index 0835133d5..000000000 --- a/js/url_search_params.ts +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { URL } from "./url.ts"; -import { requiredArguments, isIterable } from "./util.ts"; - -export class URLSearchParams { - private params: Array<[string, string]> = []; - private url: URL | null = null; - - constructor(init: string | string[][] | Record<string, string> = "") { - if (typeof init === "string") { - this._handleStringInitialization(init); - return; - } - - if (Array.isArray(init) || isIterable(init)) { - this._handleArrayInitialization(init); - return; - } - - if (Object(init) !== init) { - return; - } - - if (init instanceof URLSearchParams) { - this.params = init.params; - return; - } - - // Overload: record<USVString, USVString> - for (const key of Object.keys(init)) { - this.append(key, init[key]); - } - } - - private updateSteps(): void { - if (this.url === null) { - return; - } - - let query: string | null = this.toString(); - if (query === "") { - query = null; - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (this.url as any)._parts.query = query; - } - - /** Appends a specified key/value pair as a new search parameter. - * - * searchParams.append('name', 'first'); - * searchParams.append('name', 'second'); - */ - append(name: string, value: string): void { - requiredArguments("URLSearchParams.append", arguments.length, 2); - this.params.push([String(name), String(value)]); - this.updateSteps(); - } - - /** Deletes the given search parameter and its associated value, - * from the list of all search parameters. - * - * searchParams.delete('name'); - */ - delete(name: string): void { - requiredArguments("URLSearchParams.delete", arguments.length, 1); - name = String(name); - let i = 0; - while (i < this.params.length) { - if (this.params[i][0] === name) { - this.params.splice(i, 1); - } else { - i++; - } - } - this.updateSteps(); - } - - /** Returns all the values associated with a given search parameter - * as an array. - * - * searchParams.getAll('name'); - */ - getAll(name: string): string[] { - requiredArguments("URLSearchParams.getAll", arguments.length, 1); - name = String(name); - const values = []; - for (const entry of this.params) { - if (entry[0] === name) { - values.push(entry[1]); - } - } - - return values; - } - - /** Returns the first value associated to the given search parameter. - * - * searchParams.get('name'); - */ - get(name: string): string | null { - requiredArguments("URLSearchParams.get", arguments.length, 1); - name = String(name); - for (const entry of this.params) { - if (entry[0] === name) { - return entry[1]; - } - } - - return null; - } - - /** Returns a Boolean that indicates whether a parameter with the - * specified name exists. - * - * searchParams.has('name'); - */ - has(name: string): boolean { - requiredArguments("URLSearchParams.has", arguments.length, 1); - name = String(name); - return this.params.some((entry): boolean => entry[0] === name); - } - - /** Sets the value associated with a given search parameter to the - * given value. If there were several matching values, this method - * deletes the others. If the search parameter doesn't exist, this - * method creates it. - * - * searchParams.set('name', 'value'); - */ - set(name: string, value: string): void { - requiredArguments("URLSearchParams.set", arguments.length, 2); - - // If there are any name-value pairs whose name is name, in list, - // set the value of the first such name-value pair to value - // and remove the others. - name = String(name); - value = String(value); - let found = false; - let i = 0; - while (i < this.params.length) { - if (this.params[i][0] === name) { - if (!found) { - this.params[i][1] = value; - found = true; - i++; - } else { - this.params.splice(i, 1); - } - } else { - i++; - } - } - - // Otherwise, append a new name-value pair whose name is name - // and value is value, to list. - if (!found) { - this.append(name, value); - } - - this.updateSteps(); - } - - /** Sort all key/value pairs contained in this object in place and - * return undefined. The sort order is according to Unicode code - * points of the keys. - * - * searchParams.sort(); - */ - sort(): void { - this.params = this.params.sort( - (a, b): number => (a[0] === b[0] ? 0 : a[0] > b[0] ? 1 : -1) - ); - this.updateSteps(); - } - - /** Calls a function for each element contained in this object in - * place and return undefined. Optionally accepts an object to use - * as this when executing callback as second argument. - * - * searchParams.forEach((value, key, parent) => { - * console.log(value, key, parent); - * }); - * - */ - forEach( - callbackfn: (value: string, key: string, parent: this) => void, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - thisArg?: any - ): void { - requiredArguments("URLSearchParams.forEach", arguments.length, 1); - - if (typeof thisArg !== "undefined") { - callbackfn = callbackfn.bind(thisArg); - } - - for (const [key, value] of this.entries()) { - callbackfn(value, key, this); - } - } - - /** Returns an iterator allowing to go through all keys contained - * in this object. - * - * for (const key of searchParams.keys()) { - * console.log(key); - * } - */ - *keys(): IterableIterator<string> { - for (const entry of this.params) { - yield entry[0]; - } - } - - /** Returns an iterator allowing to go through all values contained - * in this object. - * - * for (const value of searchParams.values()) { - * console.log(value); - * } - */ - *values(): IterableIterator<string> { - for (const entry of this.params) { - yield entry[1]; - } - } - - /** Returns an iterator allowing to go through all key/value - * pairs contained in this object. - * - * for (const [key, value] of searchParams.entries()) { - * console.log(key, value); - * } - */ - *entries(): IterableIterator<[string, string]> { - yield* this.params; - } - - /** Returns an iterator allowing to go through all key/value - * pairs contained in this object. - * - * for (const [key, value] of searchParams[Symbol.iterator]()) { - * console.log(key, value); - * } - */ - *[Symbol.iterator](): IterableIterator<[string, string]> { - yield* this.params; - } - - /** Returns a query string suitable for use in a URL. - * - * searchParams.toString(); - */ - toString(): string { - return this.params - .map( - (tuple): string => - `${encodeURIComponent(tuple[0])}=${encodeURIComponent(tuple[1])}` - ) - .join("&"); - } - - private _handleStringInitialization(init: string): void { - // Overload: USVString - // If init is a string and starts with U+003F (?), - // remove the first code point from init. - if (init.charCodeAt(0) === 0x003f) { - init = init.slice(1); - } - - for (const pair of init.split("&")) { - // Empty params are ignored - if (pair.length === 0) { - continue; - } - const position = pair.indexOf("="); - const name = pair.slice(0, position === -1 ? pair.length : position); - const value = pair.slice(name.length + 1); - this.append(decodeURIComponent(name), decodeURIComponent(value)); - } - } - - private _handleArrayInitialization( - init: string[][] | Iterable<[string, string]> - ): void { - // Overload: sequence<sequence<USVString>> - for (const tuple of init) { - // If pair does not contain exactly two items, then throw a TypeError. - if (tuple.length !== 2) { - throw new TypeError( - "URLSearchParams.constructor tuple array argument must only contain pair elements" - ); - } - this.append(tuple[0], tuple[1]); - } - } -} diff --git a/js/url_search_params_test.ts b/js/url_search_params_test.ts deleted file mode 100644 index 08b0c5a1f..000000000 --- a/js/url_search_params_test.ts +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, assert, assertEquals } from "./test_util.ts"; - -test(function urlSearchParamsInitString(): void { - const init = "c=4&a=2&b=3&%C3%A1=1"; - const searchParams = new URLSearchParams(init); - assert( - init === searchParams.toString(), - "The init query string does not match" - ); -}); - -test(function urlSearchParamsInitIterable(): void { - const init = [["a", "54"], ["b", "true"]]; - const searchParams = new URLSearchParams(init); - assertEquals(searchParams.toString(), "a=54&b=true"); -}); - -test(function urlSearchParamsInitRecord(): void { - const init = { a: "54", b: "true" }; - const searchParams = new URLSearchParams(init); - assertEquals(searchParams.toString(), "a=54&b=true"); -}); - -test(function urlSearchParamsInit(): void { - const params1 = new URLSearchParams("a=b"); - assertEquals(params1.toString(), "a=b"); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const params2 = new URLSearchParams(params1 as any); - assertEquals(params2.toString(), "a=b"); -}); - -test(function urlSearchParamsAppendSuccess(): void { - const searchParams = new URLSearchParams(); - searchParams.append("a", "true"); - assertEquals(searchParams.toString(), "a=true"); -}); - -test(function urlSearchParamsDeleteSuccess(): void { - const init = "a=54&b=true"; - const searchParams = new URLSearchParams(init); - searchParams.delete("b"); - assertEquals(searchParams.toString(), "a=54"); -}); - -test(function urlSearchParamsGetAllSuccess(): void { - const init = "a=54&b=true&a=true"; - const searchParams = new URLSearchParams(init); - assertEquals(searchParams.getAll("a"), ["54", "true"]); - assertEquals(searchParams.getAll("b"), ["true"]); - assertEquals(searchParams.getAll("c"), []); -}); - -test(function urlSearchParamsGetSuccess(): void { - const init = "a=54&b=true&a=true"; - const searchParams = new URLSearchParams(init); - assertEquals(searchParams.get("a"), "54"); - assertEquals(searchParams.get("b"), "true"); - assertEquals(searchParams.get("c"), null); -}); - -test(function urlSearchParamsHasSuccess(): void { - const init = "a=54&b=true&a=true"; - const searchParams = new URLSearchParams(init); - assert(searchParams.has("a")); - assert(searchParams.has("b")); - assert(!searchParams.has("c")); -}); - -test(function urlSearchParamsSetReplaceFirstAndRemoveOthers(): void { - const init = "a=54&b=true&a=true"; - const searchParams = new URLSearchParams(init); - searchParams.set("a", "false"); - assertEquals(searchParams.toString(), "a=false&b=true"); -}); - -test(function urlSearchParamsSetAppendNew(): void { - const init = "a=54&b=true&a=true"; - const searchParams = new URLSearchParams(init); - searchParams.set("c", "foo"); - assertEquals(searchParams.toString(), "a=54&b=true&a=true&c=foo"); -}); - -test(function urlSearchParamsSortSuccess(): void { - const init = "c=4&a=2&b=3&a=1"; - const searchParams = new URLSearchParams(init); - searchParams.sort(); - assertEquals(searchParams.toString(), "a=2&a=1&b=3&c=4"); -}); - -test(function urlSearchParamsForEachSuccess(): void { - const init = [["a", "54"], ["b", "true"]]; - const searchParams = new URLSearchParams(init); - let callNum = 0; - searchParams.forEach( - (value, key, parent): void => { - assertEquals(searchParams, parent); - assertEquals(value, init[callNum][1]); - assertEquals(key, init[callNum][0]); - callNum++; - } - ); - assertEquals(callNum, init.length); -}); - -test(function urlSearchParamsMissingName(): void { - const init = "=4"; - const searchParams = new URLSearchParams(init); - assertEquals(searchParams.get(""), "4"); - assertEquals(searchParams.toString(), "=4"); -}); - -test(function urlSearchParamsMissingValue(): void { - const init = "4="; - const searchParams = new URLSearchParams(init); - assertEquals(searchParams.get("4"), ""); - assertEquals(searchParams.toString(), "4="); -}); - -test(function urlSearchParamsMissingEqualSign(): void { - const init = "4"; - const searchParams = new URLSearchParams(init); - assertEquals(searchParams.get("4"), ""); - assertEquals(searchParams.toString(), "4="); -}); - -test(function urlSearchParamsMissingPair(): void { - const init = "c=4&&a=54&"; - const searchParams = new URLSearchParams(init); - assertEquals(searchParams.toString(), "c=4&a=54"); -}); - -// If pair does not contain exactly two items, then throw a TypeError. -// ref https://url.spec.whatwg.org/#interface-urlsearchparams -test(function urlSearchParamsShouldThrowTypeError(): void { - let hasThrown = 0; - - try { - new URLSearchParams([["1"]]); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - - assertEquals(hasThrown, 2); - - try { - new URLSearchParams([["1", "2", "3"]]); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - - assertEquals(hasThrown, 2); -}); - -test(function urlSearchParamsAppendArgumentsCheck(): void { - const methodRequireOneParam = ["delete", "getAll", "get", "has", "forEach"]; - - const methodRequireTwoParams = ["append", "set"]; - - methodRequireOneParam.concat(methodRequireTwoParams).forEach( - (method: string): void => { - const searchParams = new URLSearchParams(); - let hasThrown = 0; - try { - searchParams[method](); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); - } - ); - - methodRequireTwoParams.forEach( - (method: string): void => { - const searchParams = new URLSearchParams(); - let hasThrown = 0; - try { - searchParams[method]("foo"); - hasThrown = 1; - } catch (err) { - if (err instanceof TypeError) { - hasThrown = 2; - } else { - hasThrown = 3; - } - } - assertEquals(hasThrown, 2); - } - ); -}); - -// ref: https://github.com/web-platform-tests/wpt/blob/master/url/urlsearchparams-delete.any.js -test(function urlSearchParamsDeletingAppendedMultiple(): void { - const params = new URLSearchParams(); - params.append("first", (1 as unknown) as string); - assert(params.has("first")); - assertEquals(params.get("first"), "1"); - params.delete("first"); - assertEquals(params.has("first"), false); - params.append("first", (1 as unknown) as string); - params.append("first", (10 as unknown) as string); - params.delete("first"); - assertEquals(params.has("first"), false); -}); - -// ref: https://github.com/web-platform-tests/wpt/blob/master/url/urlsearchparams-constructor.any.js#L176-L182 -test(function urlSearchParamsCustomSymbolIterator(): void { - const params = new URLSearchParams(); - params[Symbol.iterator] = function*(): IterableIterator<[string, string]> { - yield ["a", "b"]; - }; - const params1 = new URLSearchParams((params as unknown) as string[][]); - assertEquals(params1.get("a"), "b"); -}); - -test(function urlSearchParamsCustomSymbolIteratorWithNonStringParams(): void { - const params = {}; - params[Symbol.iterator] = function*(): IterableIterator<[number, number]> { - yield [1, 2]; - }; - const params1 = new URLSearchParams((params as unknown) as string[][]); - assertEquals(params1.get("1"), "2"); -}); diff --git a/js/url_test.ts b/js/url_test.ts deleted file mode 100644 index 07a8028ce..000000000 --- a/js/url_test.ts +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { test, assert, assertEquals } from "./test_util.ts"; - -test(function urlParsing(): void { - const url = new URL( - "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" - ); - assertEquals(url.hash, "#qat"); - assertEquals(url.host, "baz.qat:8000"); - assertEquals(url.hostname, "baz.qat"); - assertEquals( - url.href, - "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" - ); - assertEquals(url.origin, "https://baz.qat:8000"); - assertEquals(url.password, "bar"); - assertEquals(url.pathname, "/qux/quux"); - assertEquals(url.port, "8000"); - assertEquals(url.protocol, "https:"); - assertEquals(url.search, "?foo=bar&baz=12"); - assertEquals(url.searchParams.getAll("foo"), ["bar"]); - assertEquals(url.searchParams.getAll("baz"), ["12"]); - assertEquals(url.username, "foo"); - assertEquals( - String(url), - "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" - ); - assertEquals( - JSON.stringify({ key: url }), - `{"key":"https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat"}` - ); -}); - -test(function urlModifications(): void { - const url = new URL( - "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" - ); - url.hash = ""; - assertEquals( - url.href, - "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12" - ); - url.host = "qat.baz:8080"; - assertEquals( - url.href, - "https://foo:bar@qat.baz:8080/qux/quux?foo=bar&baz=12" - ); - url.hostname = "foo.bar"; - assertEquals( - url.href, - "https://foo:bar@foo.bar:8080/qux/quux?foo=bar&baz=12" - ); - url.password = "qux"; - assertEquals( - url.href, - "https://foo:qux@foo.bar:8080/qux/quux?foo=bar&baz=12" - ); - url.pathname = "/foo/bar%qat"; - assertEquals( - url.href, - "https://foo:qux@foo.bar:8080/foo/bar%qat?foo=bar&baz=12" - ); - url.port = ""; - assertEquals(url.href, "https://foo:qux@foo.bar/foo/bar%qat?foo=bar&baz=12"); - url.protocol = "http:"; - assertEquals(url.href, "http://foo:qux@foo.bar/foo/bar%qat?foo=bar&baz=12"); - url.search = "?foo=bar&foo=baz"; - assertEquals(url.href, "http://foo:qux@foo.bar/foo/bar%qat?foo=bar&foo=baz"); - assertEquals(url.searchParams.getAll("foo"), ["bar", "baz"]); - url.username = "foo@bar"; - assertEquals( - url.href, - "http://foo%40bar:qux@foo.bar/foo/bar%qat?foo=bar&foo=baz" - ); - url.searchParams.set("bar", "qat"); - assertEquals( - url.href, - "http://foo%40bar:qux@foo.bar/foo/bar%qat?foo=bar&foo=baz&bar=qat" - ); - url.searchParams.delete("foo"); - assertEquals(url.href, "http://foo%40bar:qux@foo.bar/foo/bar%qat?bar=qat"); - url.searchParams.append("foo", "bar"); - assertEquals( - url.href, - "http://foo%40bar:qux@foo.bar/foo/bar%qat?bar=qat&foo=bar" - ); -}); - -test(function urlModifyHref(): void { - const url = new URL("http://example.com/"); - url.href = "https://foo:bar@example.com:8080/baz/qat#qux"; - assertEquals(url.protocol, "https:"); - assertEquals(url.username, "foo"); - assertEquals(url.password, "bar"); - assertEquals(url.host, "example.com:8080"); - assertEquals(url.hostname, "example.com"); - assertEquals(url.pathname, "/baz/qat"); - assertEquals(url.hash, "#qux"); -}); - -test(function urlModifyPathname(): void { - const url = new URL("http://foo.bar/baz%qat/qux%quux"); - assertEquals(url.pathname, "/baz%qat/qux%quux"); - url.pathname = url.pathname; - assertEquals(url.pathname, "/baz%qat/qux%quux"); - url.pathname = "baz#qat qux"; - assertEquals(url.pathname, "/baz%23qat%20qux"); - url.pathname = url.pathname; - assertEquals(url.pathname, "/baz%23qat%20qux"); -}); - -test(function urlModifyHash(): void { - const url = new URL("http://foo.bar"); - url.hash = "%foo bar/qat%qux#bar"; - assertEquals(url.hash, "#%foo%20bar/qat%qux#bar"); - url.hash = url.hash; - assertEquals(url.hash, "#%foo%20bar/qat%qux#bar"); -}); - -test(function urlSearchParamsReuse(): void { - const url = new URL( - "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" - ); - const sp = url.searchParams; - url.host = "baz.qat"; - assert(sp === url.searchParams, "Search params should be reused."); -}); - -test(function urlBaseURL(): void { - const base = new URL( - "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" - ); - const url = new URL("/foo/bar?baz=foo#qux", base); - assertEquals(url.href, "https://foo:bar@baz.qat:8000/foo/bar?baz=foo#qux"); -}); - -test(function urlBaseString(): void { - const url = new URL( - "/foo/bar?baz=foo#qux", - "https://foo:bar@baz.qat:8000/qux/quux?foo=bar&baz=12#qat" - ); - assertEquals(url.href, "https://foo:bar@baz.qat:8000/foo/bar?baz=foo#qux"); -}); - -test(function urlRelativeWithBase(): void { - assertEquals(new URL("", "file:///a/a/a").href, "file:///a/a/a"); - assertEquals(new URL(".", "file:///a/a/a").href, "file:///a/a/"); - assertEquals(new URL("..", "file:///a/a/a").href, "file:///a/"); - assertEquals(new URL("b", "file:///a/a/a").href, "file:///a/a/b"); - assertEquals(new URL("b", "file:///a/a/a/").href, "file:///a/a/a/b"); - assertEquals(new URL("b/", "file:///a/a/a").href, "file:///a/a/b/"); - assertEquals(new URL("../b", "file:///a/a/a").href, "file:///a/b"); -}); - -test(function emptyBasePath(): void { - assertEquals(new URL("", "http://example.com").href, "http://example.com/"); -}); - -test(function deletingAllParamsRemovesQuestionMarkFromURL(): void { - const url = new URL("http://example.com/?param1¶m2"); - url.searchParams.delete("param1"); - url.searchParams.delete("param2"); - assertEquals(url.href, "http://example.com/"); - assertEquals(url.search, ""); -}); - -test(function removingNonExistentParamRemovesQuestionMarkFromURL(): void { - const url = new URL("http://example.com/?"); - assertEquals(url.href, "http://example.com/?"); - url.searchParams.delete("param1"); - assertEquals(url.href, "http://example.com/"); - assertEquals(url.search, ""); -}); - -test(function sortingNonExistentParamRemovesQuestionMarkFromURL(): void { - const url = new URL("http://example.com/?"); - assertEquals(url.href, "http://example.com/?"); - url.searchParams.sort(); - assertEquals(url.href, "http://example.com/"); - assertEquals(url.search, ""); -}); diff --git a/js/util.ts b/js/util.ts deleted file mode 100644 index 013dc7ee1..000000000 --- a/js/util.ts +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { TypedArray } from "./types.ts"; -import { window } from "./window.ts"; - -let logDebug = false; -let logSource = "JS"; - -// @internal -export function setLogDebug(debug: boolean, source?: string): void { - logDebug = debug; - if (source) { - logSource = source; - } -} - -/** Debug logging for deno. - * Enable with the `--log-debug` or `-D` command line flag. - * @internal - */ -export function log(...args: unknown[]): void { - if (logDebug) { - // if we destructure `console` off `window` too early, we don't bind to - // the right console, therefore we don't log anything out. - window.console.log(`DEBUG ${logSource} -`, ...args); - } -} - -// @internal -export function assert(cond: boolean, msg = "assert"): void { - if (!cond) { - throw Error(msg); - } -} - -// @internal -export function typedArrayToArrayBuffer(ta: TypedArray): ArrayBuffer { - const ab = ta.buffer.slice(ta.byteOffset, ta.byteOffset + ta.byteLength); - return ab as ArrayBuffer; -} - -// @internal -export function arrayToStr(ui8: Uint8Array): string { - return String.fromCharCode(...ui8); -} - -/** A `Resolvable` is a Promise with the `reject` and `resolve` functions - * placed as methods on the promise object itself. It allows you to do: - * - * const p = createResolvable<number>(); - * // ... - * p.resolve(42); - * - * It'd be prettier to make `Resolvable` a class that inherits from `Promise`, - * rather than an interface. This is possible in ES2016, however typescript - * produces broken code when targeting ES5 code. - * - * At the time of writing, the GitHub issue is closed in favour of a proposed - * solution that is awaiting feedback. - * - * @see https://github.com/Microsoft/TypeScript/issues/15202 - * @see https://github.com/Microsoft/TypeScript/issues/15397 - * @internal - */ - -export interface ResolvableMethods<T> { - resolve: (value?: T | PromiseLike<T>) => void; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - reject: (reason?: any) => void; -} - -// @internal -export type Resolvable<T> = Promise<T> & ResolvableMethods<T>; - -// @internal -export function createResolvable<T>(): Resolvable<T> { - let methods: ResolvableMethods<T>; - const promise = new Promise<T>( - (resolve, reject): void => { - methods = { resolve, reject }; - } - ); - // TypeScript doesn't know that the Promise callback occurs synchronously - // therefore use of not null assertion (`!`) - return Object.assign(promise, methods!) as Resolvable<T>; -} - -// @internal -export function notImplemented(): never { - throw new Error("Not implemented"); -} - -// @internal -export function unreachable(): never { - throw new Error("Code not reachable"); -} - -// @internal -export function hexdump(u8: Uint8Array): string { - return Array.prototype.map - .call( - u8, - (x: number): string => { - return ("00" + x.toString(16)).slice(-2); - } - ) - .join(" "); -} - -// @internal -export function containsOnlyASCII(str: string): boolean { - if (typeof str !== "string") { - return false; - } - return /^[\x00-\x7F]*$/.test(str); -} - -const TypedArrayConstructor = Object.getPrototypeOf(Uint8Array); -export function isTypedArray(x: unknown): x is TypedArray { - return x instanceof TypedArrayConstructor; -} - -// Returns whether o is an object, not null, and not a function. -// @internal -export function isObject(o: unknown): o is object { - return o != null && typeof o === "object"; -} - -// Returns whether o is iterable. -export function isIterable<T, P extends keyof T, K extends T[P]>( - o: T -): o is T & Iterable<[P, K]> { - // checks for null and undefined - if (o == null) { - return false; - } - return ( - typeof ((o as unknown) as Iterable<[P, K]>)[Symbol.iterator] === "function" - ); -} - -// @internal -export function requiredArguments( - name: string, - length: number, - required: number -): void { - if (length < required) { - const errMsg = `${name} requires at least ${required} argument${ - required === 1 ? "" : "s" - }, but only ${length} present`; - throw new TypeError(errMsg); - } -} - -// @internal -export function immutableDefine( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - o: any, - p: string | number | symbol, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - value: any -): void { - Object.defineProperty(o, p, { - value, - configurable: false, - writable: false - }); -} - -// Returns values from a WeakMap to emulate private properties in JavaScript -export function getPrivateValue< - K extends object, - V extends object, - W extends keyof V ->(instance: K, weakMap: WeakMap<K, V>, key: W): V[W] { - if (weakMap.has(instance)) { - return weakMap.get(instance)![key]; - } - throw new TypeError("Illegal invocation"); -} - -/** - * Determines whether an object has a property with the specified name. - * Avoid calling prototype builtin `hasOwnProperty` for two reasons: - * - * 1. `hasOwnProperty` is defined on the object as something else: - * - * const options = { - * ending: 'utf8', - * hasOwnProperty: 'foo' - * }; - * options.hasOwnProperty('ending') // throws a TypeError - * - * 2. The object doesn't inherit from `Object.prototype`: - * - * const options = Object.create(null); - * options.ending = 'utf8'; - * options.hasOwnProperty('ending'); // throws a TypeError - * - * @param obj A Object. - * @param v A property name. - * @see https://eslint.org/docs/rules/no-prototype-builtins - * @internal - */ -export function hasOwnProperty<T>(obj: T, v: PropertyKey): boolean { - if (obj == null) { - return false; - } - return Object.prototype.hasOwnProperty.call(obj, v); -} - -/** - * Split a number into two parts: lower 32 bit and higher 32 bit - * (as if the number is represented as uint64.) - * - * @param n Number to split. - * @internal - */ -export function splitNumberToParts(n: number): number[] { - // JS bitwise operators (OR, SHIFT) operate as if number is uint32. - const lower = n | 0; - // This is also faster than Math.floor(n / 0x100000000) in V8. - const higher = (n - lower) / 0x100000000; - return [lower, higher]; -} diff --git a/js/utime.ts b/js/utime.ts deleted file mode 100644 index 7495378b1..000000000 --- a/js/utime.ts +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { sendSync, sendAsync } from "./dispatch_json.ts"; -import { OP_UTIME } from "./dispatch.ts"; - -function toSecondsFromEpoch(v: number | Date): number { - return v instanceof Date ? v.valueOf() / 1000 : v; -} - -/** Synchronously changes the access and modification times of a file system - * object referenced by `filename`. Given times are either in seconds - * (Unix epoch time) or as `Date` objects. - * - * Deno.utimeSync("myfile.txt", 1556495550, new Date()); - */ -export function utimeSync( - filename: string, - atime: number | Date, - mtime: number | Date -): void { - sendSync(OP_UTIME, { - filename, - // TODO(ry) split atime, mtime into [seconds, nanoseconds] tuple - atime: toSecondsFromEpoch(atime), - mtime: toSecondsFromEpoch(mtime) - }); -} - -/** Changes the access and modification times of a file system object - * referenced by `filename`. Given times are either in seconds - * (Unix epoch time) or as `Date` objects. - * - * await Deno.utime("myfile.txt", 1556495550, new Date()); - */ -export async function utime( - filename: string, - atime: number | Date, - mtime: number | Date -): Promise<void> { - await sendAsync(OP_UTIME, { - filename, - // TODO(ry) split atime, mtime into [seconds, nanoseconds] tuple - atime: toSecondsFromEpoch(atime), - mtime: toSecondsFromEpoch(mtime) - }); -} diff --git a/js/utime_test.ts b/js/utime_test.ts deleted file mode 100644 index 535ee1f40..000000000 --- a/js/utime_test.ts +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { testPerm, assert, assertEquals } from "./test_util.ts"; - -// Allow 10 second difference. -// Note this might not be enough for FAT (but we are not testing on such fs). -function assertFuzzyTimestampEquals(t1: number, t2: number): void { - assert(Math.abs(t1 - t2) < 10); -} - -testPerm({ read: true, write: true }, function utimeSyncFileSuccess(): void { - const testDir = Deno.makeTempDirSync(); - const filename = testDir + "/file.txt"; - Deno.writeFileSync(filename, new TextEncoder().encode("hello"), { - perm: 0o666 - }); - - const atime = 1000; - const mtime = 50000; - Deno.utimeSync(filename, atime, mtime); - - const fileInfo = Deno.statSync(filename); - assertFuzzyTimestampEquals(fileInfo.accessed, atime); - assertFuzzyTimestampEquals(fileInfo.modified, mtime); -}); - -testPerm( - { read: true, write: true }, - function utimeSyncDirectorySuccess(): void { - const testDir = Deno.makeTempDirSync(); - - const atime = 1000; - const mtime = 50000; - Deno.utimeSync(testDir, atime, mtime); - - const dirInfo = Deno.statSync(testDir); - assertFuzzyTimestampEquals(dirInfo.accessed, atime); - assertFuzzyTimestampEquals(dirInfo.modified, mtime); - } -); - -testPerm({ read: true, write: true }, function utimeSyncDateSuccess(): void { - const testDir = Deno.makeTempDirSync(); - - const atime = 1000; - const mtime = 50000; - Deno.utimeSync(testDir, new Date(atime * 1000), new Date(mtime * 1000)); - - const dirInfo = Deno.statSync(testDir); - assertFuzzyTimestampEquals(dirInfo.accessed, atime); - assertFuzzyTimestampEquals(dirInfo.modified, mtime); -}); - -testPerm( - { read: true, write: true }, - function utimeSyncLargeNumberSuccess(): void { - const testDir = Deno.makeTempDirSync(); - - // There are Rust side caps (might be fs relate), - // so JUST make them slightly larger than UINT32_MAX. - const atime = 0x100000001; - const mtime = 0x100000002; - Deno.utimeSync(testDir, atime, mtime); - - const dirInfo = Deno.statSync(testDir); - assertFuzzyTimestampEquals(dirInfo.accessed, atime); - assertFuzzyTimestampEquals(dirInfo.modified, mtime); - } -); - -testPerm({ read: true, write: true }, function utimeSyncNotFound(): void { - const atime = 1000; - const mtime = 50000; - - let caughtError = false; - try { - Deno.utimeSync("/baddir", atime, mtime); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.NotFound); - assertEquals(e.name, "NotFound"); - } - assert(caughtError); -}); - -testPerm({ read: true, write: false }, function utimeSyncPerm(): void { - const atime = 1000; - const mtime = 50000; - - let caughtError = false; - try { - Deno.utimeSync("/some_dir", atime, mtime); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); - -testPerm( - { read: true, write: true }, - async function utimeFileSuccess(): Promise<void> { - const testDir = Deno.makeTempDirSync(); - const filename = testDir + "/file.txt"; - Deno.writeFileSync(filename, new TextEncoder().encode("hello"), { - perm: 0o666 - }); - - const atime = 1000; - const mtime = 50000; - await Deno.utime(filename, atime, mtime); - - const fileInfo = Deno.statSync(filename); - assertFuzzyTimestampEquals(fileInfo.accessed, atime); - assertFuzzyTimestampEquals(fileInfo.modified, mtime); - } -); - -testPerm( - { read: true, write: true }, - async function utimeDirectorySuccess(): Promise<void> { - const testDir = Deno.makeTempDirSync(); - - const atime = 1000; - const mtime = 50000; - await Deno.utime(testDir, atime, mtime); - - const dirInfo = Deno.statSync(testDir); - assertFuzzyTimestampEquals(dirInfo.accessed, atime); - assertFuzzyTimestampEquals(dirInfo.modified, mtime); - } -); - -testPerm( - { read: true, write: true }, - async function utimeDateSuccess(): Promise<void> { - const testDir = Deno.makeTempDirSync(); - - const atime = 1000; - const mtime = 50000; - await Deno.utime(testDir, new Date(atime * 1000), new Date(mtime * 1000)); - - const dirInfo = Deno.statSync(testDir); - assertFuzzyTimestampEquals(dirInfo.accessed, atime); - assertFuzzyTimestampEquals(dirInfo.modified, mtime); - } -); - -testPerm({ read: true, write: true }, async function utimeNotFound(): Promise< - void -> { - const atime = 1000; - const mtime = 50000; - - let caughtError = false; - try { - await Deno.utime("/baddir", atime, mtime); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.NotFound); - assertEquals(e.name, "NotFound"); - } - assert(caughtError); -}); - -testPerm({ read: true, write: false }, async function utimeSyncPerm(): Promise< - void -> { - const atime = 1000; - const mtime = 50000; - - let caughtError = false; - try { - await Deno.utime("/some_dir", atime, mtime); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); diff --git a/js/version.ts b/js/version.ts deleted file mode 100644 index 08ac58122..000000000 --- a/js/version.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -interface Version { - deno: string; - v8: string; - typescript: string; -} - -export const version: Version = { - deno: "", - v8: "", - typescript: "" -}; - -/** - * Sets the deno, v8, and typescript versions and freezes the version object. - * @internal - */ -export function setVersions( - denoVersion: string, - v8Version: string, - tsVersion: string -): void { - version.deno = denoVersion; - version.v8 = v8Version; - version.typescript = tsVersion; - - Object.freeze(version); -} diff --git a/js/version_test.ts b/js/version_test.ts deleted file mode 100644 index b32230812..000000000 --- a/js/version_test.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { test, assert } from "./test_util.ts"; - -test(function version(): void { - const pattern = /^\d+\.\d+\.\d+/; - assert(pattern.test(Deno.version.deno)); - assert(pattern.test(Deno.version.v8)); - assert(pattern.test(Deno.version.typescript)); -}); diff --git a/js/window.ts b/js/window.ts deleted file mode 100644 index 3d3d6601f..000000000 --- a/js/window.ts +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -// (0, eval) is indirect eval. -// See the links below for details: -// - https://stackoverflow.com/a/14120023 -// - https://tc39.github.io/ecma262/#sec-performeval (spec) -export const window = (0, eval)("this"); -// TODO: The above should be replaced with globalThis -// when the globalThis proposal goes to stage 4 -// See https://github.com/tc39/proposal-global diff --git a/js/workers.ts b/js/workers.ts deleted file mode 100644 index 281fe619f..000000000 --- a/js/workers.ts +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -/* eslint-disable @typescript-eslint/no-explicit-any */ -import * as dispatch from "./dispatch.ts"; -import { sendAsync, sendSync } from "./dispatch_json.ts"; -import { log } from "./util.ts"; -import { TextDecoder, TextEncoder } from "./text_encoding.ts"; -import { window } from "./window.ts"; -import { blobURLMap } from "./url.ts"; -import { blobBytesWeakMap } from "./blob.ts"; - -const encoder = new TextEncoder(); -const decoder = new TextDecoder(); - -export function encodeMessage(data: any): Uint8Array { - const dataJson = JSON.stringify(data); - return encoder.encode(dataJson); -} - -export function decodeMessage(dataIntArray: Uint8Array): any { - const dataJson = decoder.decode(dataIntArray); - return JSON.parse(dataJson); -} - -function createWorker( - specifier: string, - includeDenoNamespace: boolean, - hasSourceCode: boolean, - sourceCode: Uint8Array -): number { - return sendSync(dispatch.OP_CREATE_WORKER, { - specifier, - includeDenoNamespace, - hasSourceCode, - sourceCode: new TextDecoder().decode(sourceCode) - }); -} - -async function hostGetWorkerClosed(rid: number): Promise<void> { - await sendAsync(dispatch.OP_HOST_GET_WORKER_CLOSED, { rid }); -} - -function hostPostMessage(rid: number, data: any): void { - const dataIntArray = encodeMessage(data); - sendSync(dispatch.OP_HOST_POST_MESSAGE, { rid }, dataIntArray); -} - -async function hostGetMessage(rid: number): Promise<any> { - const res = await sendAsync(dispatch.OP_HOST_GET_MESSAGE, { rid }); - - if (res.data != null) { - return decodeMessage(new Uint8Array(res.data)); - } else { - return null; - } -} - -// Stuff for workers -export const onmessage: (e: { data: any }) => void = (): void => {}; - -export function postMessage(data: any): void { - const dataIntArray = encodeMessage(data); - sendSync(dispatch.OP_WORKER_POST_MESSAGE, {}, dataIntArray); -} - -export async function getMessage(): Promise<any> { - log("getMessage"); - const res = await sendAsync(dispatch.OP_WORKER_GET_MESSAGE); - - if (res.data != null) { - return decodeMessage(new Uint8Array(res.data)); - } else { - return null; - } -} - -export let isClosing = false; - -export function workerClose(): void { - isClosing = true; -} - -export async function workerMain(): Promise<void> { - log("workerMain"); - - while (!isClosing) { - const data = await getMessage(); - if (data == null) { - log("workerMain got null message. quitting."); - break; - } - - if (window["onmessage"]) { - const event = { data }; - const result: void | Promise<void> = window.onmessage(event); - if (result && "then" in result) { - await result; - } - } - - if (!window["onmessage"]) { - break; - } - } -} - -export interface Worker { - onerror?: () => void; - onmessage?: (e: { data: any }) => void; - onmessageerror?: () => void; - postMessage(data: any): void; - closed: Promise<void>; -} - -// TODO(kevinkassimo): Maybe implement reasonable web worker options? -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface WorkerOptions {} - -/** Extended Deno Worker initialization options. - * `noDenoNamespace` hides global `window.Deno` namespace for - * spawned worker and nested workers spawned by it (default: false). - */ -export interface DenoWorkerOptions extends WorkerOptions { - noDenoNamespace?: boolean; -} - -export class WorkerImpl implements Worker { - private readonly rid: number; - private isClosing = false; - private readonly isClosedPromise: Promise<void>; - public onerror?: () => void; - public onmessage?: (data: any) => void; - public onmessageerror?: () => void; - - constructor(specifier: string, options?: DenoWorkerOptions) { - let hasSourceCode = false; - let sourceCode = new Uint8Array(); - - let includeDenoNamespace = true; - if (options && options.noDenoNamespace) { - includeDenoNamespace = false; - } - // Handle blob URL. - if (specifier.startsWith("blob:")) { - hasSourceCode = true; - const b = blobURLMap.get(specifier); - if (!b) { - throw new Error("No Blob associated with the given URL is found"); - } - const blobBytes = blobBytesWeakMap.get(b!); - if (!blobBytes) { - throw new Error("Invalid Blob"); - } - sourceCode = blobBytes!; - } - - this.rid = createWorker( - specifier, - includeDenoNamespace, - hasSourceCode, - sourceCode - ); - this.run(); - this.isClosedPromise = hostGetWorkerClosed(this.rid); - this.isClosedPromise.then( - (): void => { - this.isClosing = true; - } - ); - } - - get closed(): Promise<void> { - return this.isClosedPromise; - } - - postMessage(data: any): void { - hostPostMessage(this.rid, data); - } - - private async run(): Promise<void> { - while (!this.isClosing) { - const data = await hostGetMessage(this.rid); - if (data == null) { - log("worker got null message. quitting."); - break; - } - // TODO(afinch7) stop this from eating messages before onmessage has been assigned - if (this.onmessage) { - const event = { data }; - this.onmessage(event); - } - } - } -} diff --git a/js/write_file.ts b/js/write_file.ts deleted file mode 100644 index d6307e002..000000000 --- a/js/write_file.ts +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { stat, statSync } from "./stat.ts"; -import { open, openSync } from "./files.ts"; -import { chmod, chmodSync } from "./chmod.ts"; -import { writeAll, writeAllSync } from "./buffer.ts"; - -/** Options for writing to a file. - * `perm` would change the file's permission if set. - * `create` decides if the file should be created if not exists (default: true) - * `append` decides if the file should be appended (default: false) - */ -export interface WriteFileOptions { - perm?: number; - create?: boolean; - append?: boolean; -} - -/** Write a new file, with given filename and data synchronously. - * - * const encoder = new TextEncoder(); - * const data = encoder.encode("Hello world\n"); - * Deno.writeFileSync("hello.txt", data); - */ -export function writeFileSync( - filename: string, - data: Uint8Array, - options: WriteFileOptions = {} -): void { - if (options.create !== undefined) { - const create = !!options.create; - if (!create) { - // verify that file exists - statSync(filename); - } - } - - const openMode = !!options.append ? "a" : "w"; - const file = openSync(filename, openMode); - - if (options.perm !== undefined && options.perm !== null) { - chmodSync(filename, options.perm); - } - - writeAllSync(file, data); - file.close(); -} - -/** Write a new file, with given filename and data. - * - * const encoder = new TextEncoder(); - * const data = encoder.encode("Hello world\n"); - * await Deno.writeFile("hello.txt", data); - */ -export async function writeFile( - filename: string, - data: Uint8Array, - options: WriteFileOptions = {} -): Promise<void> { - if (options.create !== undefined) { - const create = !!options.create; - if (!create) { - // verify that file exists - await stat(filename); - } - } - - const openMode = !!options.append ? "a" : "w"; - const file = await open(filename, openMode); - - if (options.perm !== undefined && options.perm !== null) { - await chmod(filename, options.perm); - } - - await writeAll(file, data); - file.close(); -} diff --git a/js/write_file_test.ts b/js/write_file_test.ts deleted file mode 100644 index e1bbb67b3..000000000 --- a/js/write_file_test.ts +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. -import { testPerm, assert, assertEquals } from "./test_util.ts"; - -testPerm({ read: true, write: true }, function writeFileSyncSuccess(): void { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - Deno.writeFileSync(filename, data); - const dataRead = Deno.readFileSync(filename); - const dec = new TextDecoder("utf-8"); - const actual = dec.decode(dataRead); - assertEquals("Hello", actual); -}); - -testPerm({ write: true }, function writeFileSyncFail(): void { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = "/baddir/test.txt"; - // The following should fail because /baddir doesn't exist (hopefully). - let caughtError = false; - try { - Deno.writeFileSync(filename, data); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.NotFound); - assertEquals(e.name, "NotFound"); - } - assert(caughtError); -}); - -testPerm({ write: false }, function writeFileSyncPerm(): void { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = "/baddir/test.txt"; - // The following should fail due to no write permission - let caughtError = false; - try { - Deno.writeFileSync(filename, data); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); - -testPerm({ read: true, write: true }, function writeFileSyncUpdatePerm(): void { - if (Deno.build.os !== "win") { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - Deno.writeFileSync(filename, data, { perm: 0o755 }); - assertEquals(Deno.statSync(filename).mode & 0o777, 0o755); - Deno.writeFileSync(filename, data, { perm: 0o666 }); - assertEquals(Deno.statSync(filename).mode & 0o777, 0o666); - } -}); - -testPerm({ read: true, write: true }, function writeFileSyncCreate(): void { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - let caughtError = false; - // if create turned off, the file won't be created - try { - Deno.writeFileSync(filename, data, { create: false }); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.NotFound); - assertEquals(e.name, "NotFound"); - } - assert(caughtError); - - // Turn on create, should have no error - Deno.writeFileSync(filename, data, { create: true }); - Deno.writeFileSync(filename, data, { create: false }); - const dataRead = Deno.readFileSync(filename); - const dec = new TextDecoder("utf-8"); - const actual = dec.decode(dataRead); - assertEquals("Hello", actual); -}); - -testPerm({ read: true, write: true }, function writeFileSyncAppend(): void { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - Deno.writeFileSync(filename, data); - Deno.writeFileSync(filename, data, { append: true }); - let dataRead = Deno.readFileSync(filename); - const dec = new TextDecoder("utf-8"); - let actual = dec.decode(dataRead); - assertEquals("HelloHello", actual); - // Now attempt overwrite - Deno.writeFileSync(filename, data, { append: false }); - dataRead = Deno.readFileSync(filename); - actual = dec.decode(dataRead); - assertEquals("Hello", actual); - // append not set should also overwrite - Deno.writeFileSync(filename, data); - dataRead = Deno.readFileSync(filename); - actual = dec.decode(dataRead); - assertEquals("Hello", actual); -}); - -testPerm( - { read: true, write: true }, - async function writeFileSuccess(): Promise<void> { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - await Deno.writeFile(filename, data); - const dataRead = Deno.readFileSync(filename); - const dec = new TextDecoder("utf-8"); - const actual = dec.decode(dataRead); - assertEquals("Hello", actual); - } -); - -testPerm( - { read: true, write: true }, - async function writeFileNotFound(): Promise<void> { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = "/baddir/test.txt"; - // The following should fail because /baddir doesn't exist (hopefully). - let caughtError = false; - try { - await Deno.writeFile(filename, data); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.NotFound); - assertEquals(e.name, "NotFound"); - } - assert(caughtError); - } -); - -testPerm({ read: true, write: false }, async function writeFilePerm(): Promise< - void -> { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = "/baddir/test.txt"; - // The following should fail due to no write permission - let caughtError = false; - try { - await Deno.writeFile(filename, data); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.PermissionDenied); - assertEquals(e.name, "PermissionDenied"); - } - assert(caughtError); -}); - -testPerm( - { read: true, write: true }, - async function writeFileUpdatePerm(): Promise<void> { - if (Deno.build.os !== "win") { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - await Deno.writeFile(filename, data, { perm: 0o755 }); - assertEquals(Deno.statSync(filename).mode & 0o777, 0o755); - await Deno.writeFile(filename, data, { perm: 0o666 }); - assertEquals(Deno.statSync(filename).mode & 0o777, 0o666); - } - } -); - -testPerm({ read: true, write: true }, async function writeFileCreate(): Promise< - void -> { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - let caughtError = false; - // if create turned off, the file won't be created - try { - await Deno.writeFile(filename, data, { create: false }); - } catch (e) { - caughtError = true; - assertEquals(e.kind, Deno.ErrorKind.NotFound); - assertEquals(e.name, "NotFound"); - } - assert(caughtError); - - // Turn on create, should have no error - await Deno.writeFile(filename, data, { create: true }); - await Deno.writeFile(filename, data, { create: false }); - const dataRead = Deno.readFileSync(filename); - const dec = new TextDecoder("utf-8"); - const actual = dec.decode(dataRead); - assertEquals("Hello", actual); -}); - -testPerm({ read: true, write: true }, async function writeFileAppend(): Promise< - void -> { - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - const filename = Deno.makeTempDirSync() + "/test.txt"; - await Deno.writeFile(filename, data); - await Deno.writeFile(filename, data, { append: true }); - let dataRead = Deno.readFileSync(filename); - const dec = new TextDecoder("utf-8"); - let actual = dec.decode(dataRead); - assertEquals("HelloHello", actual); - // Now attempt overwrite - await Deno.writeFile(filename, data, { append: false }); - dataRead = Deno.readFileSync(filename); - actual = dec.decode(dataRead); - assertEquals("Hello", actual); - // append not set should also overwrite - await Deno.writeFile(filename, data); - dataRead = Deno.readFileSync(filename); - actual = dec.decode(dataRead); - assertEquals("Hello", actual); -}); |