From 3179eb6848ba11697333923bbf3850c3d0aeb975 Mon Sep 17 00:00:00 2001 From: Tim Caswell Date: Thu, 20 Feb 2020 10:47:08 -0600 Subject: [PATCH 1/3] Start migrating more to ts --- package.json | 4 +-- src/assert.js => src-ts/assert.ts | 4 +-- src/evented.js => src-ts/evented.ts | 17 ++++++++----- src/.gitignore | 3 ++- src/timers.d.ts | 39 ----------------------------- 5 files changed, 16 insertions(+), 51 deletions(-) rename src/assert.js => src-ts/assert.ts (56%) rename src/evented.js => src-ts/evented.ts (71%) delete mode 100644 src/timers.d.ts diff --git a/package.json b/package.json index d4454ca..ba81b7c 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "module": "src/polyfills.js", "dependencies": {}, "devDependencies": { - "magic-script-typings": "^1.6.2", + "magic-script-typings": "^1.6.4", "typescript": "^3.7.3" }, "scripts": { @@ -30,4 +30,4 @@ "url": "https://github.com/magic-script/magic-script-polyfills/issues" }, "homepage": "https://github.com/magic-script/magic-script-polyfills#readme" -} \ No newline at end of file +} diff --git a/src/assert.js b/src-ts/assert.ts similarity index 56% rename from src/assert.js rename to src-ts/assert.ts index 2f11a23..9743774 100644 --- a/src/assert.js +++ b/src-ts/assert.ts @@ -1,9 +1,7 @@ /** * lua-style assert helper - * @param {any} val - * @param {String} message */ -export function assert (val, message) { +export function assert(val: any, message?: string): asserts val { if (!val) throw new Error(message || 'Assertion Failed'); return val; } diff --git a/src/evented.js b/src-ts/evented.ts similarity index 71% rename from src/evented.js rename to src-ts/evented.ts index 4d13c59..acdb0b6 100644 --- a/src/evented.js +++ b/src-ts/evented.ts @@ -1,18 +1,23 @@ -let handlersKey = Symbol('EventHandlersKey'); +const handlersKey = Symbol('EventHandlersKey'); + +type Handler = (evt: any) => void; export class Evented { - constructor () { + [handlersKey]: { [key: string]: Handler[] } + [key: string]: any + + constructor() { Object.defineProperty(this, handlersKey, { value: {} }); } - addEventListener (name, handler) { + addEventListener(name: string, handler: Handler) { const eventHandlers = this[handlersKey]; let list = eventHandlers[name]; if (!list) eventHandlers[name] = list = []; list.push(handler); } - removeEventListener (name, handler) { + removeEventListener(name: string, handler: Handler) { const list = this[handlersKey][name]; if (!list) return; const index = list.indexOf(handler); @@ -20,7 +25,7 @@ export class Evented { list.splice(index, 1); } - emit (name, val) { + emit(name: string, val: any): void { const key = `on${name}`; let handlers = []; if (this[key]) handlers.push(this[key]); @@ -28,7 +33,7 @@ export class Evented { if (eventHandlers[name]) handlers = handlers.concat(eventHandlers[name]); if (handlers.length === 0) { if (name === 'error') { - return Promise.reject(val); + Promise.reject(val); } return; } diff --git a/src/.gitignore b/src/.gitignore index d94710b..66a0886 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -10,4 +10,5 @@ /pretty-print.* /crypto.* /Storage.* -/fs-sync.* \ No newline at end of file +/fs-sync.* +/evented.* diff --git a/src/timers.d.ts b/src/timers.d.ts deleted file mode 100644 index 49a08b8..0000000 --- a/src/timers.d.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Timer } from "uv"; -/** - * Sets a timer which executes a function or specified piece of code once the timer expires. - * @param fn A function to be executed after the timer expires. - * @param delay The time, in milliseconds, the timer should wait before the specified function or code is executed. - * @param args Additional parameters which are passed through to the function once the timer expires. - */ -export declare function setTimeout(fn: (...args: any[]) => void, delay: number, ...args: any[]): Timer; -/** - * Cancels a timeout previously established by calling setTimeout(). - * @param timer The identifier of the timeout you want to cancel. - */ -export declare function clearTimeout(timer: Timer): void; -/** - * Repeatedly calls a function or executes a code snippet, with a fixed time delay between each call. - * @param fn A function to be executed every delay milliseconds. - * @param delay The time, in milliseconds, the timer should delay in between - * executions of the specified function or code - * @param args Additional parameters which are passed through to the function once the timer expires. - */ -export declare function setInterval(fn: (...args: any[]) => void, delay: number, ...args: any[]): Timer; -/** - * Cancels an interval previously established by calling setInterval(). - * @param timer The identifier of the interval you want to cancel. - */ -export declare function clearInterval(timer: Timer): void; -/** - * This method is used to defer running some code till after the current JS stack. - * Unlike using promises, this is lighter weight and doesn't swallow errors. - * @param fn the function you wish to call later. - * @param args Additional parameters which are passed through to the function when called. - * @returns the ID used for clearing the immediate. - */ -export declare function setImmediate(fn: (...args: any[]) => void, ...args: any[]): () => void; -/** - * Clear the immediate actions, just like window.clearTimeout for window.setTimeout. - * @param id the ID returned from setImmediate - */ -export declare function clearImmediate(id: () => void): void; From c382b2992d504661dd9c254d3fe845288e08763c Mon Sep 17 00:00:00 2001 From: Tim Caswell Date: Thu, 20 Feb 2020 10:56:09 -0600 Subject: [PATCH 2/3] Move the rest of the source files to ts-src --- src/bintools.js => src-ts/bintools.ts | 0 src/codec-wrap.js => src-ts/codec-wrap.ts | 0 src/console.js => src-ts/console.ts | 0 src/fetch.js => src-ts/fetch.ts | 0 src/fs.js => src-ts/fs.ts | 0 src/headers.js => src-ts/headers.ts | 0 src/http-codec.js => src-ts/http-codec.ts | 0 src/index.js => src-ts/index.ts | 0 src/mime.js => src-ts/mime.ts | 0 src/pathjoin.js => src-ts/pathjoin.ts | 0 src/polyfills.js => src-ts/polyfills.ts | 0 src/resolve.js => src-ts/resolve.ts | 0 src/sha1.js => src-ts/sha1.ts | 0 src/socket-wrap.js => src-ts/socket-wrap.ts | 0 src/tcp.js => src-ts/tcp.ts | 0 src/text-encoder.js => src-ts/text-encoder.ts | 0 src/tls-wrap.js => src-ts/tls-wrap.ts | 0 src/utils.js => src-ts/utils.ts | 0 src/weblit-tools.js => src-ts/weblit-tools.ts | 0 .../websocket-codec.ts | 0 src/websocket.js => src-ts/websocket.ts | 0 src/xhr.js => src-ts/xhr.ts | 0 src/.gitignore | 15 +-------------- 23 files changed, 1 insertion(+), 14 deletions(-) rename src/bintools.js => src-ts/bintools.ts (100%) rename src/codec-wrap.js => src-ts/codec-wrap.ts (100%) rename src/console.js => src-ts/console.ts (100%) rename src/fetch.js => src-ts/fetch.ts (100%) rename src/fs.js => src-ts/fs.ts (100%) rename src/headers.js => src-ts/headers.ts (100%) rename src/http-codec.js => src-ts/http-codec.ts (100%) rename src/index.js => src-ts/index.ts (100%) rename src/mime.js => src-ts/mime.ts (100%) rename src/pathjoin.js => src-ts/pathjoin.ts (100%) rename src/polyfills.js => src-ts/polyfills.ts (100%) rename src/resolve.js => src-ts/resolve.ts (100%) rename src/sha1.js => src-ts/sha1.ts (100%) rename src/socket-wrap.js => src-ts/socket-wrap.ts (100%) rename src/tcp.js => src-ts/tcp.ts (100%) rename src/text-encoder.js => src-ts/text-encoder.ts (100%) rename src/tls-wrap.js => src-ts/tls-wrap.ts (100%) rename src/utils.js => src-ts/utils.ts (100%) rename src/weblit-tools.js => src-ts/weblit-tools.ts (100%) rename src/websocket-codec.js => src-ts/websocket-codec.ts (100%) rename src/websocket.js => src-ts/websocket.ts (100%) rename src/xhr.js => src-ts/xhr.ts (100%) diff --git a/src/bintools.js b/src-ts/bintools.ts similarity index 100% rename from src/bintools.js rename to src-ts/bintools.ts diff --git a/src/codec-wrap.js b/src-ts/codec-wrap.ts similarity index 100% rename from src/codec-wrap.js rename to src-ts/codec-wrap.ts diff --git a/src/console.js b/src-ts/console.ts similarity index 100% rename from src/console.js rename to src-ts/console.ts diff --git a/src/fetch.js b/src-ts/fetch.ts similarity index 100% rename from src/fetch.js rename to src-ts/fetch.ts diff --git a/src/fs.js b/src-ts/fs.ts similarity index 100% rename from src/fs.js rename to src-ts/fs.ts diff --git a/src/headers.js b/src-ts/headers.ts similarity index 100% rename from src/headers.js rename to src-ts/headers.ts diff --git a/src/http-codec.js b/src-ts/http-codec.ts similarity index 100% rename from src/http-codec.js rename to src-ts/http-codec.ts diff --git a/src/index.js b/src-ts/index.ts similarity index 100% rename from src/index.js rename to src-ts/index.ts diff --git a/src/mime.js b/src-ts/mime.ts similarity index 100% rename from src/mime.js rename to src-ts/mime.ts diff --git a/src/pathjoin.js b/src-ts/pathjoin.ts similarity index 100% rename from src/pathjoin.js rename to src-ts/pathjoin.ts diff --git a/src/polyfills.js b/src-ts/polyfills.ts similarity index 100% rename from src/polyfills.js rename to src-ts/polyfills.ts diff --git a/src/resolve.js b/src-ts/resolve.ts similarity index 100% rename from src/resolve.js rename to src-ts/resolve.ts diff --git a/src/sha1.js b/src-ts/sha1.ts similarity index 100% rename from src/sha1.js rename to src-ts/sha1.ts diff --git a/src/socket-wrap.js b/src-ts/socket-wrap.ts similarity index 100% rename from src/socket-wrap.js rename to src-ts/socket-wrap.ts diff --git a/src/tcp.js b/src-ts/tcp.ts similarity index 100% rename from src/tcp.js rename to src-ts/tcp.ts diff --git a/src/text-encoder.js b/src-ts/text-encoder.ts similarity index 100% rename from src/text-encoder.js rename to src-ts/text-encoder.ts diff --git a/src/tls-wrap.js b/src-ts/tls-wrap.ts similarity index 100% rename from src/tls-wrap.js rename to src-ts/tls-wrap.ts diff --git a/src/utils.js b/src-ts/utils.ts similarity index 100% rename from src/utils.js rename to src-ts/utils.ts diff --git a/src/weblit-tools.js b/src-ts/weblit-tools.ts similarity index 100% rename from src/weblit-tools.js rename to src-ts/weblit-tools.ts diff --git a/src/websocket-codec.js b/src-ts/websocket-codec.ts similarity index 100% rename from src/websocket-codec.js rename to src-ts/websocket-codec.ts diff --git a/src/websocket.js b/src-ts/websocket.ts similarity index 100% rename from src/websocket.js rename to src-ts/websocket.ts diff --git a/src/xhr.js b/src-ts/xhr.ts similarity index 100% rename from src/xhr.js rename to src-ts/xhr.ts diff --git a/src/.gitignore b/src/.gitignore index 66a0886..f59ec20 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,14 +1 @@ -*.d.ts -*.js.map -/node/ -/fs-promised.* -/fs-uv.* -/TextDecoder.* -/TextEncoder.* -/utils2.* -/timers.* -/pretty-print.* -/crypto.* -/Storage.* -/fs-sync.* -/evented.* +* \ No newline at end of file From 1d22da193c6e1f1760335035b222032e86cd8d15 Mon Sep 17 00:00:00 2001 From: Tim Caswell Date: Thu, 20 Feb 2020 20:27:33 -0600 Subject: [PATCH 3/3] Save progress --- src-ts/evented.ts | 2 +- src-ts/socket-wrap.ts | 31 +++++++++++++++++-------- src-ts/tcp.ts | 7 +++--- src-ts/tls-wrap.ts | 25 ++++++++++---------- src-ts/websocket.ts | 53 ++++++++++++++++++++++--------------------- src-ts/xhr.ts | 8 +++---- 6 files changed, 69 insertions(+), 57 deletions(-) diff --git a/src-ts/evented.ts b/src-ts/evented.ts index acdb0b6..56e8d3a 100644 --- a/src-ts/evented.ts +++ b/src-ts/evented.ts @@ -25,7 +25,7 @@ export class Evented { list.splice(index, 1); } - emit(name: string, val: any): void { + emit(name: string, val?: any): void { const key = `on${name}`; let handlers = []; if (this[key]) handlers.push(this[key]); diff --git a/src-ts/socket-wrap.ts b/src-ts/socket-wrap.ts index 64a8548..73c2d16 100644 --- a/src-ts/socket-wrap.ts +++ b/src-ts/socket-wrap.ts @@ -1,9 +1,20 @@ -import { Write, Shutdown } from 'uv'; +import { Write, Shutdown, Stream } from 'uv'; import { assert } from './assert.js'; +type Out = { err?: Error, val?: ArrayBuffer } +type In = { resolve: (val?: ArrayBuffer) => void, reject: (err: Error) => void }; +type Item = Out | In; + +export interface IStream { + read: () => Promise, + write: (val?: ArrayBuffer) => Promise, + close: () => Promise, + socket: Stream +}; + // Socket is a libuv socket class (like a TCP socket) // Output is promise based read/write functions. -export function socketWrap (socket) { +export function socketWrap(socket: Stream): IStream { assert(socket && socket.write && socket.shutdown && socket.readStart && socket.readStop && socket.close, 'Missing stream functions'); let writeClosed = false; let readClosed = false; @@ -11,13 +22,13 @@ export function socketWrap (socket) { let reading = false; // If writer > reader, there is data to be read. // if reader > writer, there is data required. - let queue = []; + let queue: Item[] = []; let reader = 0; let writer = 0; return { read, write, socket, close }; - async function close () { + async function close() { readClosed = true; if (!writeClosed) { await write(); @@ -26,17 +37,17 @@ export function socketWrap (socket) { } } - function check () { + function check() { if (readClosed && writeClosed && !socketClosed) { socketClosed = true; socket.close(); } } - function onData (err, val) { + function onData(err: Error, val?: ArrayBuffer) { // If there is a pending reader, give it the data right away. if (reader > writer) { - let { resolve, reject } = queue[writer]; + let { resolve, reject } = queue[writer] as In queue[writer++] = undefined; if (err) return reject(err); readClosed = !val; @@ -53,11 +64,11 @@ export function socketWrap (socket) { queue[writer++] = { err, val }; } - async function read () { + async function read(): Promise { if (socketClosed || readClosed) throw new Error('Cannot read from closed socket'); // If there is pending data, return it right away. if (writer > reader) { - let { err, val } = queue[reader]; + let { err, val } = queue[reader] as Out; queue[reader++] = undefined; if (err) throw err; return val; @@ -76,7 +87,7 @@ export function socketWrap (socket) { } // write(ArrayBuffer | null) -> Promise - function write (buffer) { + function write(buffer?: ArrayBuffer): Promise { return new Promise((resolve, reject) => { if (socketClosed || writeClosed) throw new Error('Cannot write to closed socket'); if (buffer) { diff --git a/src-ts/tcp.ts b/src-ts/tcp.ts index 7842cd6..7553f2f 100644 --- a/src-ts/tcp.ts +++ b/src-ts/tcp.ts @@ -1,7 +1,6 @@ -/// import { Getaddrinfo, getaddrinfo, Connect, Tcp } from 'uv'; -function makeCallback () { +function makeCallback() { let callback, promise; promise = new Promise((resolve, reject) => { callback = (err, val) => err ? reject(err) : resolve(val); @@ -12,7 +11,7 @@ function makeCallback () { const cache = {}; -export async function resolve (host, service) { +export async function resolve(host, service) { // Resolve IP address and TCP port const key = host + service; if (!cache[key]) { @@ -23,7 +22,7 @@ export async function resolve (host, service) { return cache[key]; } -export async function connect (host, service) { +export async function connect(host, service) { const { ip, port } = await resolve(host, service); // Connect to server let socket = new Tcp(); diff --git a/src-ts/tls-wrap.ts b/src-ts/tls-wrap.ts index c92ad78..e50ffc9 100644 --- a/src-ts/tls-wrap.ts +++ b/src-ts/tls-wrap.ts @@ -1,14 +1,15 @@ import { Ssl } from 'ssl'; import { assert } from './assert.js'; +import { IStream } from "./socket-wrap.js"; // Input read/write // read() -> Promise // write(ArrayBuffer|null) -> Promise -export async function tlsWrap ({ read: innerRead, write: innerWrite, close: innerClose, ...rest }, hostname) { +export async function tlsWrap({ read: innerRead, write: innerWrite, close: innerClose, ...rest }: IStream, hostname?: string): Promise { assert(innerRead); assert(innerWrite); - read.inner = innerRead; - write.inner = innerWrite; + // read.inner = innerRead; + // write.inner = innerWrite; let ssl = new Ssl(); if (hostname) ssl.setHostname(hostname); @@ -16,7 +17,7 @@ export async function tlsWrap ({ read: innerRead, write: innerWrite, close: inne return { read, write, close, ...rest }; - async function handshake () { + async function handshake() { let status; while ((status = ssl.getError(ssl.doHandshake())) !== 'SSL_ERROR_NONE') { if (status === 'SSL_ERROR_WANT_READ') { @@ -40,22 +41,22 @@ export async function tlsWrap ({ read: innerRead, write: innerWrite, close: inne } } - async function close () { - if (ssl.shutdown) { - ssl.shutdown(); + async function close() { + if (ssl.sslShutdown) { + ssl.sslShutdown(); } ssl = null; await innerClose(); } - async function flush () { + async function flush() { let data; while ((data = ssl.bioRead())) { if (data) await innerWrite(data); } } - async function read () { + async function read(): Promise { let buffer = new ArrayBuffer(16384); let result; while ((result = ssl.sslRead(buffer))) { @@ -78,14 +79,14 @@ export async function tlsWrap ({ read: innerRead, write: innerWrite, close: inne } } - async function write (data) { + async function write(data?: ArrayBuffer) { let shouldShutdown = false; if (data) { ssl.sslWrite(data); } else { shouldShutdown = true; - if (ssl.shutdown) { - ssl.shutdown(); + if (ssl.sslShutdown) { + ssl.sslShutdown(); } } while ((data = ssl.bioRead())) { diff --git a/src-ts/websocket.ts b/src-ts/websocket.ts index 7ca051a..82b7dd4 100644 --- a/src-ts/websocket.ts +++ b/src-ts/websocket.ts @@ -8,31 +8,37 @@ import { Evented } from './evented.js'; import { Headers } from './headers.js'; import { encodeRaw, decodeRaw, acceptKey } from './websocket-codec.js'; -let writeKey = Symbol('Websocket::Write'); -let closedKey = Symbol('Websocket::Closed'); -let streamKey = Symbol('Websocket::Stream'); +const writeKey = Symbol('Websocket::Write'); +const closedKey = Symbol('Websocket::Closed'); +const streamKey = Symbol('Websocket::Stream'); export class WebSocket extends Evented { + private [writeKey]: any; + private [closedKey]: any; + private [streamKey]: any; + static CONNECTING = 0; + static OPEN = 1; + static CLOSED = 2; + static CLOSING = 3; /** * Establish a websocket client connection. - * @param {string} url the ws(s) url of the remote server - * @param {string|Array} protocols optional websocket subprotocol(s) + * @param url the ws(s) url of the remote server + * @param protocols optional websocket subprotocol(s) */ - constructor (url, protocols) { + constructor(url: string, protocols?: string[]) { const urlParts = url.match(/^ws(s?):\/\/([^:/]+)(:[0-9]+)?(\/[^?#]*)?([?][^#]*)?(#.*)?$/); if (!urlParts) { throw new Error('Not a valid websocket URL: ' + url); } const [, secure, host, port, path, query] = urlParts; super(); - this.doHandshake({ secure, host, port, path, query }, protocols) + this.doHandshake(secure, host, port, path, query, protocols) .catch(err => this.emit('error', err)); } - async doHandshake ({ secure = false, host = 'localhost', port, path, query } = {}, protocols) { - secure = !!secure; + async doHandshake(secure: string, host: string, rawPort: string, path: string, query: string, protocols?: string[] | string) { const defaultPort = secure ? 443 : 80; - port = port ? parseInt(port.substr(1), 10) : defaultPort; + const port = rawPort ? parseInt(rawPort.substr(1), 10) : defaultPort; path = path || '/'; query = query || ''; protocols = protocols ? Array.isArray(protocols) ? protocols : [protocols] : []; @@ -46,11 +52,11 @@ export class WebSocket extends Evented { enumerable: true }); - let key = new Uint8Array(21); + let keyBuf = new Uint8Array(21); for (let i = 0; i < 21; i++) { - key[i] = Math.random() * 256; + keyBuf[i] = Math.random() * 256; } - key = binToB64(key); + const key = binToB64(keyBuf); const headers = [ 'Host', origin, @@ -105,13 +111,13 @@ export class WebSocket extends Evented { this.readyState = WebSocket.OPEN; this.emit('open'); - function makeMessageEvent(data) { + function makeMessageEvent(data: Uint8Array) { return { data, origin: '', lastEventId: '', - source: null, - ports: [] + source: null as string | null, + ports: [] as number[] } } @@ -132,7 +138,7 @@ export class WebSocket extends Evented { if (!this[closedKey]) this.close(); break; case 9: // ping - this[writeKey]({ opcode: 10, mask: true }).catch(err => this.emit('error', err)); + this[writeKey]({ opcode: 10, mask: true }).catch((err: Error) => this.emit('error', err)); break; } } @@ -142,17 +148,17 @@ export class WebSocket extends Evented { this.emit('close'); } - close (payload = '') { + close(payload = '') { if (this.readyState !== WebSocket.OPEN) { throw new Error('Attempt to close Websocket when not open'); } this.readyState = WebSocket.CLOSING; this[closedKey] = true; - this[writeKey]({ opcode: 8, mask: true, payload }).catch(err => this.emit('error', err)); + this[writeKey]({ opcode: 8, mask: true, payload }).catch((err: Error) => this.emit('error', err)); this[writeKey](); } - send (message) { + send(message: string | Uint8Array | ArrayBuffer) { if (this.readyState !== WebSocket.OPEN) { throw new Error('Attempt to send Websocket frame when not open'); } @@ -172,12 +178,7 @@ export class WebSocket extends Evented { } else { throw new TypeError('Expected string or binary value in websocket send'); } - this[writeKey](frame).catch(err => this.emit('error', err)); + this[writeKey](frame).catch((err: Error) => this.emit('error', err)); } } WebSocket.prototype.binaryType = 'arraybuffer'; - -WebSocket.CONNECTING = 0; -WebSocket.OPEN = 1; -WebSocket.CLOSING = 2; -WebSocket.CLOSED = 3; diff --git a/src-ts/xhr.ts b/src-ts/xhr.ts index 417bceb..5840285 100644 --- a/src-ts/xhr.ts +++ b/src-ts/xhr.ts @@ -16,7 +16,7 @@ export class XMLHttpRequest extends Evented { } // Stores method and url - open(method, url) { + open(method: string, url: string) { this.readyState = OPENED; this.method = method; this.url = url; @@ -34,7 +34,7 @@ export class XMLHttpRequest extends Evented { } // Append header to headers object - setRequestHeader(name, value) { + setRequestHeader(name: string, value: string) { this.headers[name] = value; } @@ -54,7 +54,7 @@ export class XMLHttpRequest extends Evented { * Uses fetch to fire request * Fires both load and readystatechange so registered components know data was loaded */ - send(body) { + send(body: any) { fetch(this.url, { method: this.method, body }) .then(res => { this.readyState = DONE; @@ -86,7 +86,7 @@ export class XMLHttpRequest extends Evented { } // Get response header by key - getResponseHeader(key) { + getResponseHeader(key: string): string | null { return this.responseHeaders.get(key); } }