Skip to content

Commit

Permalink
Improved prototype inheritance with extended classes, Improved TypeSc…
Browse files Browse the repository at this point in the history
…ript & JSDoc Types
  • Loading branch information
kartikk221 committed Oct 23, 2022
1 parent 4729070 commit 67a73dc
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 38 deletions.
8 changes: 3 additions & 5 deletions src/components/http/Request.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use strict';
const cookie = require('cookie');
const stream = require('stream');
const emitter = require('events');
const busboy = require('busboy');
const signature = require('cookie-signature');
const querystring = require('querystring');
Expand Down Expand Up @@ -485,7 +484,7 @@ class Request {
* Downloads and parses the request body as a JSON object.
* Passing default_value as undefined will lead to the function throwing an exception if invalid JSON is received.
*
* @param {Any} default_value Default: {}
* @param {Any=} default_value Default: {}
* @returns {Promise}
*/
async json(default_value = {}) {
Expand Down Expand Up @@ -872,10 +871,9 @@ inherit_prototype({
},
});

// Inherit the stream.Readable and EventEmitter prototypes
// Lazy initialize the stream.Readable instance on each call to any of the inherited methods
// Inherit the stream.Readable prototype and lazy initialize the stream on first call to inherited methods
inherit_prototype({
from: [stream.Readable.prototype, emitter.prototype],
from: stream.Readable.prototype,
to: Request.prototype,
override: (name) => '_super_' + name, // Prefix all overrides with _super_
method: (type, name, original) => {
Expand Down
6 changes: 2 additions & 4 deletions src/components/http/Response.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ const signature = require('cookie-signature');
const status_codes = require('http').STATUS_CODES;
const mime_types = require('mime-types');
const stream = require('stream');
const emitter = require('events');

const NodeResponse = require('../compatibility/NodeResponse.js');
const ExpressResponse = require('../compatibility/ExpressResponse.js');
Expand Down Expand Up @@ -884,10 +883,9 @@ inherit_prototype({
},
});

// Inherit the stream.Writable and EventEmitter prototypes
// Lazy initialize the stream.Writable instance on each call to any of the inherited methods
// Inherit the stream.Writable prototype and lazy initialize the stream on first call to any inherited method
inherit_prototype({
from: [stream.Writable.prototype, emitter.prototype],
from: stream.Writable.prototype,
to: Response.prototype,
override: (name) => '_super_' + name, // Prefix all overrides with _super_
method: (type, name, original) => {
Expand Down
24 changes: 22 additions & 2 deletions src/shared/operators.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,26 @@ function merge_relative_paths(base_path, new_path) {
return `${base_path}${new_path}`;
}

/**
* Returns all property descriptors of an Object including extended prototypes.
*
* @param {Object} prototype
*/
function get_all_property_descriptors(prototype) {
// Retrieve initial property descriptors
const descriptors = Object.getOwnPropertyDescriptors(prototype);

// Determine if we have a parent prototype with a custom name
const parent = Object.getPrototypeOf(prototype);
if (parent && parent.constructor.name !== 'Object') {
// Merge and return property descriptors along with parent prototype
return Object.assign(descriptors, get_all_property_descriptors(parent));
}

// Return property descriptors
return descriptors;
}

/**
* Inherits properties, getters, and setters from one prototype to another with the ability to optionally define middleman methods.
*
Expand All @@ -104,8 +124,8 @@ function inherit_prototype({ from, to, method, override, ignore = ['constructor'
if (Array.isArray(from)) return from.forEach((f) => inherit_prototype({ from: f, to, override, method, ignore }));

// Inherit the descriptors from the "from" prototype to the "to" prototype
const to_descriptors = Object.getOwnPropertyDescriptors(to);
const from_descriptors = Object.getOwnPropertyDescriptors(from);
const to_descriptors = get_all_property_descriptors(to);
const from_descriptors = get_all_property_descriptors(from);
Object.keys(from_descriptors).forEach((name) => {
// Ignore the properties specified in the ignore array
if (ignore.includes(name)) return;
Expand Down
16 changes: 11 additions & 5 deletions types/components/http/Request.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ParsedQs } from 'qs';
import { Readable } from 'stream';
import { BusboyConfig } from 'busboy';
import { ParamsDictionary } from 'express-serve-static-core';
import { ParsedQs } from 'qs';
import { Options, Ranges, Result } from 'range-parser';
import * as uWebsockets from 'uWebSockets.js';
import { MultipartHandler } from '../plugins/MultipartField';
Expand All @@ -9,10 +10,15 @@ import { Server } from '../Server';
type default_value = any;

type DefaultRequestLocals = {
[key: string]: any
}
[key: string]: any;
};

export class Request<Locals = DefaultRequestLocals> extends Readable {
/**
* Underlying raw lazy initialized readable body stream.
*/
_readable: null | Readable;

export class Request<Locals = DefaultRequestLocals> {
/**
* Returns whether all expected incoming request body chunks have been received.
* @returns {Boolean}
Expand Down Expand Up @@ -204,4 +210,4 @@ export class Request<Locals = DefaultRequestLocals> {
query: ParsedQs;
originalUrl: string;
baseUrl: string;
}
}
47 changes: 25 additions & 22 deletions types/components/http/Response.d.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
import * as Stream from 'stream';
import { Readable, Writable } from 'stream';
import * as uWebsockets from 'uWebSockets.js';
import { LiveFile } from '../plugins/LiveFile';
import { Server } from '../Server';
import { Request } from './Request'

export type SendableData = string | Buffer | ArrayBuffer;
export type FileCachePool = {
[key: string]: LiveFile
[key: string]: LiveFile;
};

export interface CookieOptions {
domain?: string,
path?: string,
maxAge?: number,
secure?: boolean,
httpOnly?: boolean,
sameSite?: boolean | 'none' | 'lax' | 'strict'
secret?: string
domain?: string;
path?: string;
maxAge?: number;
secure?: boolean;
httpOnly?: boolean;
sameSite?: boolean | 'none' | 'lax' | 'strict';
secret?: string;
}

type DefaultResponseLocals = {
[key: string]: any
}
[key: string]: any;
};

export class Response<Locals = DefaultResponseLocals> extends Writable {
/**
* Underlying raw lazy initialized writable stream.
*/
_writable: null | Writable;

export class Response<Locals = DefaultResponseLocals> extends Stream.Writable {
/**
* Alias of aborted property as they both represent the same request state in terms of inaccessibility.
* @returns {Boolean}
*/
completed: boolean;

Expand Down Expand Up @@ -108,10 +111,10 @@ export class Response<Locals = DefaultResponseLocals> extends Stream.Writable {
* By default, this method will use chunked encoding transfer to stream data.
* If your use-case requires a content-length header, you must specify the total payload size.
*
* @param {stream.Readable} readable A Readable stream which will be piped as response body
* @param {Readable} readable A Readable stream which will be piped as response body
* @param {Number=} total_size Total size of the Readable stream source in bytes (Optional)
*/
stream(readable: Stream.Readable, total_size?: number): void;
stream(readable: Readable, total_size?: number): void;

/**
* Instantly aborts/closes current request without writing a status response code.
Expand Down Expand Up @@ -187,16 +190,16 @@ export class Response<Locals = DefaultResponseLocals> extends Stream.Writable {
/**
* This method allows you to throw an error which will be caught by the global error handler (If one was setup with the Server instance).
*
* @param {Error} error
* @param {Error} error
*/
throw(error: Error): Response;

/* HyperExpress Properties */

/**
* Returns the underlying raw uWS.Response object.
* @returns {uWebsockets.Response}
*/
* Returns the underlying raw uWS.Response object.
* @returns {uWebsockets.Response}
*/
get raw(): uWebsockets.HttpResponse;

/**
Expand Down Expand Up @@ -240,7 +243,7 @@ export class Response<Locals = DefaultResponseLocals> extends Stream.Writable {
setHeaders(headers: Object): void;
writeHeaderValues(name: string, values: Array<string>): void;
getHeader(name: string): string | Array<string> | void;
getHeaders(): { [key: string]: Array<string> }
getHeaders(): { [key: string]: Array<string> };
removeHeader(name: string): void;
setCookie(name: string, value: string, options?: CookieOptions): Response;
hasCookie(name: string): Boolean;
Expand All @@ -258,4 +261,4 @@ export class Response<Locals = DefaultResponseLocals> extends Stream.Writable {
get headersSent(): boolean;
get statusCode(): number | undefined;
locals: Locals;
}
}

0 comments on commit 67a73dc

Please sign in to comment.