rjweb-server
Advanced tools
Comparing version 9.6.0 to 9.7.0
# Changelog | ||
## 9.7.0 | ||
- Fix windows route file importing | ||
- Cache empty arraybuffers | ||
- Add `<HttpRequestContext>.$body` | ||
- Add `<WsMessageContext>.$message` | ||
## 9.6.0 | ||
@@ -4,0 +11,0 @@ |
@@ -65,2 +65,36 @@ "use strict"; | ||
/** | ||
* The Request Body as a Blob-like Object | ||
* @example | ||
* ``` | ||
* const size = await ctr.$body().size() | ||
* | ||
* return ctr.print(`The size of the body is ${size} bytes`) | ||
* ``` | ||
* @since 9.7.0 | ||
*/ $body() { | ||
const self = this; | ||
return { | ||
text() { | ||
return self.rawBody('utf-8'); | ||
}, async json() { | ||
const data = await self.body(); | ||
if (self.context.body.type === 'json') | ||
return data; | ||
throw new Error('Body is not JSON (make sure the content-type sent is application/json)'); | ||
}, arrayBuffer() { | ||
return self.rawBodyBytes(); | ||
}, async formData() { | ||
const data = await self.body(); | ||
if (self.context.body.type === 'url-encoded') | ||
return data; | ||
throw new Error('Body is not Form Data (make sure the content-type sent is application/x-www-form-urlencoded)'); | ||
}, async blob() { | ||
return new Blob([await self.rawBodyBytes()]); | ||
}, async size() { | ||
const data = await self.rawBodyBytes(); | ||
return data.byteLength; | ||
} | ||
}; | ||
} | ||
/** | ||
* The Request Body (JSON and Urlencoding Automatically parsed if enabled) | ||
@@ -67,0 +101,0 @@ * @since 0.4.0 |
@@ -12,2 +12,30 @@ "use strict"; | ||
/** | ||
* The Request Message as a Blob-like Object | ||
* @example | ||
* ``` | ||
* const size = await ctr.$message().size() | ||
* | ||
* return ctr.print(`The size of the message is ${size} bytes`) | ||
* ``` | ||
* @since 9.7.0 | ||
*/ $message() { | ||
const self = this; | ||
return { | ||
text() { | ||
return self.rawMessage('utf-8'); | ||
}, json() { | ||
const data = self.message(); | ||
if (self.context.body.type === 'json') | ||
return data; | ||
throw new Error('Message is not valid JSON'); | ||
}, arrayBuffer() { | ||
return self.rawMessageBytes(); | ||
}, blob() { | ||
return new Blob([self.rawMessageBytes()]); | ||
}, size() { | ||
return self.rawMessageBytes().byteLength; | ||
} | ||
}; | ||
} | ||
/** | ||
* The Websocket Message (JSON Automatically parsed if enabled) | ||
@@ -17,3 +45,4 @@ * @since 5.4.0 | ||
const stringified = this.context.body.raw.toString(); | ||
if ((stringified.startsWith('{') && stringified.endsWith('}')) || (stringified.startsWith('[') && stringified.endsWith(']'))) { | ||
if ((stringified[0] === '{' && stringified[stringified.length - 1] === '}') || | ||
(stringified[0] === '[' && stringified[stringified.length - 1] === ']')) { | ||
try { | ||
@@ -20,0 +49,0 @@ const json = JSON.parse(stringified); |
@@ -13,2 +13,4 @@ "use strict"; | ||
const deepClone_1 = __importDefault(require("../../functions/deepClone")); | ||
const os_1 = __importDefault(require("os")); | ||
const platform = os_1.default.platform(); | ||
class File { | ||
@@ -142,3 +144,3 @@ computePath(path) { | ||
try { | ||
const { default: router } = await eval(`import(${JSON.stringify(file.path)})`), // bypass ttsc converting to require() in cjs | ||
const importPath = platform === 'win32' ? `file:///${file.path}` : file.path, { default: router } = await eval(`import(${JSON.stringify(importPath)})`), // bypass ttsc converting to require() in cjs | ||
path = file.path.replace(resolved, '').replaceAll('index', '').replace(/\\/g, '/').split('.').slice(0, -1).join('.').concat('/'); | ||
@@ -145,0 +147,0 @@ if (router instanceof Path_1.default) { |
@@ -19,3 +19,3 @@ "use strict"; | ||
* <p>${userInput}</p> | ||
* ` | ||
* ` // XSS Vulnerable | ||
* | ||
@@ -25,3 +25,3 @@ * const secure = html` | ||
* <p>${userInput}</p> | ||
* ` | ||
* ` // XSS Safe | ||
* ``` | ||
@@ -28,0 +28,0 @@ * @since 8.7.0 |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const mapping = { | ||
const mapping = Object.freeze({ | ||
pdf: 'application/pdf', | ||
@@ -82,4 +82,6 @@ js: 'text/javascript', | ||
vcf: 'text/vcard', | ||
ics: 'text/calendar' | ||
}; | ||
ics: 'text/calendar', | ||
abw: 'application/x-abiword', | ||
azw: 'application/vnd.amazon.ebook' | ||
}); | ||
/** | ||
@@ -93,8 +95,10 @@ * Parse File Name into a Content Type or empty string | ||
} | ||
const end = name.split('.').at(-1); | ||
const dot = name.lastIndexOf('.'); | ||
if (dot === -1) | ||
return 'application/octet-stream'; | ||
const end = name.slice(dot + 1); | ||
if (!end) | ||
return 'application/octet-stream'; | ||
const type = mapping[end]; | ||
return type ?? 'application/octet-stream'; | ||
return mapping[end] ?? 'application/octet-stream'; | ||
} | ||
exports.default = parseContentType; |
@@ -9,3 +9,3 @@ "use strict"; | ||
const utils_1 = require("@rjweb/utils"); | ||
const trimString = (str) => { | ||
function trimString(str) { | ||
let start = 0, end = str.length - 1; | ||
@@ -19,3 +19,3 @@ while (start < end && str[start] === ' ') { | ||
return str.substring(start, end + 1); | ||
}; | ||
} | ||
exports.trimString = trimString; | ||
@@ -45,5 +45,5 @@ /** | ||
if (type === 'ValueCollection') | ||
(0, utils_1.as)(values).set((0, exports.trimString)(keyValue.slice(progress, progress + equalPos)), decodedVal); | ||
(0, utils_1.as)(values).set(trimString(keyValue.slice(progress, progress + equalPos)), decodedVal); | ||
else | ||
(0, utils_1.as)(values)[(0, exports.trimString)(keyValue.slice(progress, progress + equalPos))] = decodedVal; | ||
(0, utils_1.as)(values)[trimString(keyValue.slice(progress, progress + equalPos))] = decodedVal; | ||
progress = splitterPos + 1; | ||
@@ -50,0 +50,0 @@ } |
@@ -48,3 +48,3 @@ "use strict"; | ||
if (context.headers.get('if-none-match') === eTag) { | ||
await rawContext.status(status_1.default.NOT_MODIFIED, 'Not Modified').write(new ArrayBuffer(0)); | ||
await rawContext.status(status_1.default.NOT_MODIFIED, 'Not Modified').write(context.global.cache.emptyArrayBuffer); | ||
return false; | ||
@@ -62,3 +62,3 @@ } | ||
.status(context.response.status, context.response.statusText || http_1.STATUS_CODES[context.response.status] || 'Unknown') | ||
.write(new ArrayBuffer(0)); | ||
.write(context.global.cache.emptyArrayBuffer); | ||
return false; | ||
@@ -65,0 +65,0 @@ } |
{ | ||
"name": "rjweb-server", | ||
"version": "9.6.0", | ||
"version": "9.7.0", | ||
"description": "Easy and Robust Way to create a Web Server with Many Easy-to-use Features in NodeJS", | ||
@@ -49,3 +49,3 @@ "main": "./lib/cjs/index.js", | ||
"@types/inquirer": "^9.0.7", | ||
"@types/node": "^20.12.12", | ||
"@types/node": "^20.14.2", | ||
"@types/yargs": "^17.0.32", | ||
@@ -61,5 +61,5 @@ "rjweb-server": "link:", | ||
"dependencies": { | ||
"@rjweb/utils": "^1.12.15", | ||
"@rjweb/utils": "^1.12.20", | ||
"content-disposition": "^0.5.4", | ||
"inquirer": "^9.2.22", | ||
"inquirer": "^9.2.23", | ||
"openapi3-ts": "^4.3.2", | ||
@@ -66,0 +66,0 @@ "yargs": "^17.7.2", |
@@ -65,2 +65,6 @@ "use strict"; | ||
/** | ||
* An Empty ArrayBuffer | ||
* @since 9.7.0 | ||
*/ emptyArrayBuffer: new ArrayBuffer(0), | ||
/** | ||
* Cached ArrayBuffer Texts | ||
@@ -67,0 +71,0 @@ * @since 9.0.0 |
@@ -11,2 +11,3 @@ "use strict"; | ||
const parseURL_1 = __importDefault(require("../../../functions/parseURL")); | ||
const empty = Buffer.allocUnsafe(0); | ||
class RequestContext { | ||
@@ -85,3 +86,3 @@ constructor(context, middlewares, server, global) { | ||
cookies: new ValueCollection_1.default(), | ||
content: Buffer.allocUnsafe(0), | ||
content: empty, | ||
prettify: false | ||
@@ -88,0 +89,0 @@ }; |
@@ -37,2 +37,36 @@ import { Status } from "../../types/global"; | ||
/** | ||
* The Request Body as a Blob-like Object | ||
* @example | ||
* ``` | ||
* const size = await ctr.$body().size() | ||
* | ||
* return ctr.print(`The size of the body is ${size} bytes`) | ||
* ``` | ||
* @since 9.7.0 | ||
*/ $body() { | ||
const self = this; | ||
return { | ||
text() { | ||
return self.rawBody('utf-8'); | ||
}, async json() { | ||
const data = await self.body(); | ||
if (self.context.body.type === 'json') | ||
return data; | ||
throw new Error('Body is not JSON (make sure the content-type sent is application/json)'); | ||
}, arrayBuffer() { | ||
return self.rawBodyBytes(); | ||
}, async formData() { | ||
const data = await self.body(); | ||
if (self.context.body.type === 'url-encoded') | ||
return data; | ||
throw new Error('Body is not Form Data (make sure the content-type sent is application/x-www-form-urlencoded)'); | ||
}, async blob() { | ||
return new Blob([await self.rawBodyBytes()]); | ||
}, async size() { | ||
const data = await self.rawBodyBytes(); | ||
return data.byteLength; | ||
} | ||
}; | ||
} | ||
/** | ||
* The Request Body (JSON and Urlencoding Automatically parsed if enabled) | ||
@@ -39,0 +73,0 @@ * @since 0.4.0 |
@@ -7,2 +7,30 @@ import WsOpenContext from "./WsOpenContext"; | ||
/** | ||
* The Request Message as a Blob-like Object | ||
* @example | ||
* ``` | ||
* const size = await ctr.$message().size() | ||
* | ||
* return ctr.print(`The size of the message is ${size} bytes`) | ||
* ``` | ||
* @since 9.7.0 | ||
*/ $message() { | ||
const self = this; | ||
return { | ||
text() { | ||
return self.rawMessage('utf-8'); | ||
}, json() { | ||
const data = self.message(); | ||
if (self.context.body.type === 'json') | ||
return data; | ||
throw new Error('Message is not valid JSON'); | ||
}, arrayBuffer() { | ||
return self.rawMessageBytes(); | ||
}, blob() { | ||
return new Blob([self.rawMessageBytes()]); | ||
}, size() { | ||
return self.rawMessageBytes().byteLength; | ||
} | ||
}; | ||
} | ||
/** | ||
* The Websocket Message (JSON Automatically parsed if enabled) | ||
@@ -12,3 +40,4 @@ * @since 5.4.0 | ||
const stringified = this.context.body.raw.toString(); | ||
if ((stringified.startsWith('{') && stringified.endsWith('}')) || (stringified.startsWith('[') && stringified.endsWith(']'))) { | ||
if ((stringified[0] === '{' && stringified[stringified.length - 1] === '}') || | ||
(stringified[0] === '[' && stringified[stringified.length - 1] === ']')) { | ||
try { | ||
@@ -15,0 +44,0 @@ const json = JSON.parse(stringified); |
@@ -8,2 +8,4 @@ import Route from "../Route"; | ||
import deepClone from "../../functions/deepClone"; | ||
import os from "os"; | ||
const platform = os.platform(); | ||
export default class File { | ||
@@ -137,3 +139,3 @@ computePath(path) { | ||
try { | ||
const { default: router } = await eval(`import(${JSON.stringify(file.path)})`), // bypass ttsc converting to require() in cjs | ||
const importPath = platform === 'win32' ? `file:///${file.path}` : file.path, { default: router } = await eval(`import(${JSON.stringify(importPath)})`), // bypass ttsc converting to require() in cjs | ||
path = file.path.replace(resolved, '').replaceAll('index', '').replace(/\\/g, '/').split('.').slice(0, -1).join('.').concat('/'); | ||
@@ -140,0 +142,0 @@ if (router instanceof Path) { |
@@ -17,3 +17,3 @@ const replace = { | ||
* <p>${userInput}</p> | ||
* ` | ||
* ` // XSS Vulnerable | ||
* | ||
@@ -23,3 +23,3 @@ * const secure = html` | ||
* <p>${userInput}</p> | ||
* ` | ||
* ` // XSS Safe | ||
* ``` | ||
@@ -26,0 +26,0 @@ * @since 8.7.0 |
@@ -1,2 +0,2 @@ | ||
const mapping = { | ||
const mapping = Object.freeze({ | ||
pdf: 'application/pdf', | ||
@@ -80,4 +80,6 @@ js: 'text/javascript', | ||
vcf: 'text/vcard', | ||
ics: 'text/calendar' | ||
}; | ||
ics: 'text/calendar', | ||
abw: 'application/x-abiword', | ||
azw: 'application/vnd.amazon.ebook' | ||
}); | ||
/** | ||
@@ -91,7 +93,9 @@ * Parse File Name into a Content Type or empty string | ||
} | ||
const end = name.split('.').at(-1); | ||
const dot = name.lastIndexOf('.'); | ||
if (dot === -1) | ||
return 'application/octet-stream'; | ||
const end = name.slice(dot + 1); | ||
if (!end) | ||
return 'application/octet-stream'; | ||
const type = mapping[end]; | ||
return type ?? 'application/octet-stream'; | ||
return mapping[end] ?? 'application/octet-stream'; | ||
} |
import ValueCollection from "../classes/ValueCollection"; | ||
import { as } from "@rjweb/utils"; | ||
export const trimString = (str) => { | ||
export function trimString(str) { | ||
let start = 0, end = str.length - 1; | ||
@@ -12,3 +12,3 @@ while (start < end && str[start] === ' ') { | ||
return str.substring(start, end + 1); | ||
}; | ||
} | ||
/** | ||
@@ -15,0 +15,0 @@ * Efficiently parse Key-Value Strings into ValueCollections |
@@ -43,3 +43,3 @@ import toETag from "./toETag"; | ||
if (context.headers.get('if-none-match') === eTag) { | ||
await rawContext.status(Status.NOT_MODIFIED, 'Not Modified').write(new ArrayBuffer(0)); | ||
await rawContext.status(Status.NOT_MODIFIED, 'Not Modified').write(context.global.cache.emptyArrayBuffer); | ||
return false; | ||
@@ -57,3 +57,3 @@ } | ||
.status(context.response.status, context.response.statusText || STATUS_CODES[context.response.status] || 'Unknown') | ||
.write(new ArrayBuffer(0)); | ||
.write(context.global.cache.emptyArrayBuffer); | ||
return false; | ||
@@ -60,0 +60,0 @@ } |
{ | ||
"name": "rjweb-server", | ||
"version": "9.6.0", | ||
"version": "9.7.0", | ||
"description": "Easy and Robust Way to create a Web Server with Many Easy-to-use Features in NodeJS", | ||
@@ -49,3 +49,3 @@ "main": "./lib/cjs/index.js", | ||
"@types/inquirer": "^9.0.7", | ||
"@types/node": "^20.12.12", | ||
"@types/node": "^20.14.2", | ||
"@types/yargs": "^17.0.32", | ||
@@ -61,5 +61,5 @@ "rjweb-server": "link:", | ||
"dependencies": { | ||
"@rjweb/utils": "^1.12.15", | ||
"@rjweb/utils": "^1.12.20", | ||
"content-disposition": "^0.5.4", | ||
"inquirer": "^9.2.22", | ||
"inquirer": "^9.2.23", | ||
"openapi3-ts": "^4.3.2", | ||
@@ -66,0 +66,0 @@ "yargs": "^17.7.2", |
@@ -60,2 +60,6 @@ import ValueCollection from "../../../classes/ValueCollection"; | ||
/** | ||
* An Empty ArrayBuffer | ||
* @since 9.7.0 | ||
*/ emptyArrayBuffer: new ArrayBuffer(0), | ||
/** | ||
* Cached ArrayBuffer Texts | ||
@@ -62,0 +66,0 @@ * @since 9.0.0 |
@@ -6,2 +6,3 @@ import Middleware from "../../../classes/Middleware"; | ||
import parseURL from "../../../functions/parseURL"; | ||
const empty = Buffer.allocUnsafe(0); | ||
export default class RequestContext { | ||
@@ -80,3 +81,3 @@ constructor(context, middlewares, server, global) { | ||
cookies: new ValueCollection(), | ||
content: Buffer.allocUnsafe(0), | ||
content: empty, | ||
prettify: false | ||
@@ -83,0 +84,0 @@ }; |
@@ -6,3 +6,3 @@ /// <reference types="node" /> | ||
import { HttpContext } from "../../types/implementation/contexts/http"; | ||
import { Content, ParsedBody, RatelimitInfos } from "../../types/global"; | ||
import { Content, JSONParsed, ParsedBody, RatelimitInfos } from "../../types/global"; | ||
import Base from "./Base"; | ||
@@ -81,3 +81,6 @@ import { ZodResponse } from "../../types/internal"; | ||
GATEWAY_TIMEOUT: 504; | ||
HTTP_VERSION_NOT_SUPPORTED: 505; | ||
/** | ||
* HTTP Abort Controller (please use to decrease server load) | ||
* @since 9.0.0 | ||
*/ HTTP_VERSION_NOT_SUPPORTED: 505; | ||
VARIANT_ALSO_NEGOTIATES: 506; | ||
@@ -95,2 +98,19 @@ INSUFFICIENT_STORAGE: 507; | ||
/** | ||
* The Request Body as a Blob-like Object | ||
* @example | ||
* ``` | ||
* const size = await ctr.$body().size() | ||
* | ||
* return ctr.print(`The size of the body is ${size} bytes`) | ||
* ``` | ||
* @since 9.7.0 | ||
*/ $body(): { | ||
text(): Promise<string>; | ||
json(): Promise<JSONParsed>; | ||
arrayBuffer(): Promise<Buffer>; | ||
formData(): Promise<Record<string, string>>; | ||
blob(): Promise<Blob>; | ||
size(): Promise<number>; | ||
}; | ||
/** | ||
* The Request Body (JSON and Urlencoding Automatically parsed if enabled) | ||
@@ -97,0 +117,0 @@ * @since 0.4.0 |
@@ -6,6 +6,22 @@ /// <reference types="node" /> | ||
import WsOpenContext from "./WsOpenContext"; | ||
import { ParsedBody, RatelimitInfos } from "../../types/global"; | ||
import { JSONParsed, ParsedBody, RatelimitInfos } from "../../types/global"; | ||
export default class WsMessageContext<Context extends Record<any, any> = {}> extends WsOpenContext<'message', Context> { | ||
constructor(context: InternalRequestContext, rawContext: WsContext, abort: AbortSignal); | ||
/** | ||
* The Request Message as a Blob-like Object | ||
* @example | ||
* ``` | ||
* const size = await ctr.$message().size() | ||
* | ||
* return ctr.print(`The size of the message is ${size} bytes`) | ||
* ``` | ||
* @since 9.7.0 | ||
*/ $message(): { | ||
text(): string; | ||
json(): JSONParsed; | ||
arrayBuffer(): Buffer; | ||
blob(): Blob; | ||
size(): number; | ||
}; | ||
/** | ||
* The Websocket Message (JSON Automatically parsed if enabled) | ||
@@ -12,0 +28,0 @@ * @since 5.4.0 |
@@ -11,3 +11,3 @@ export type HTMLContent = string | number | boolean | undefined; | ||
* <p>${userInput}</p> | ||
* ` | ||
* ` // XSS Vulnerable | ||
* | ||
@@ -17,5 +17,5 @@ * const secure = html` | ||
* <p>${userInput}</p> | ||
* ` | ||
* ` // XSS Safe | ||
* ``` | ||
* @since 8.7.0 | ||
*/ export default function html(parts: TemplateStringsArray, ...variables: HTMLContent[]): string; |
import ValueCollection from "../classes/ValueCollection"; | ||
export declare const trimString: (str: string) => string; | ||
export declare function trimString(str: string): string; | ||
/** | ||
@@ -4,0 +4,0 @@ * Efficiently parse Key-Value Strings into ValueCollections |
@@ -84,2 +84,6 @@ import Route from "../../../classes/Route"; | ||
/** | ||
* An Empty ArrayBuffer | ||
* @since 9.7.0 | ||
*/ emptyArrayBuffer: ArrayBuffer; | ||
/** | ||
* Cached ArrayBuffer Texts | ||
@@ -86,0 +90,0 @@ * @since 9.0.0 |
{ | ||
"name": "rjweb-server", | ||
"version": "9.6.0", | ||
"version": "9.7.0", | ||
"description": "Easy and Robust Way to create a Web Server with Many Easy-to-use Features in NodeJS", | ||
@@ -41,3 +41,3 @@ "main": "./lib/cjs/index.js", | ||
"@types/inquirer": "^9.0.7", | ||
"@types/node": "^20.12.12", | ||
"@types/node": "^20.14.2", | ||
"@types/yargs": "^17.0.32", | ||
@@ -53,5 +53,5 @@ "rjweb-server": "link:", | ||
"dependencies": { | ||
"@rjweb/utils": "^1.12.15", | ||
"@rjweb/utils": "^1.12.20", | ||
"content-disposition": "^0.5.4", | ||
"inquirer": "^9.2.22", | ||
"inquirer": "^9.2.23", | ||
"openapi3-ts": "^4.3.2", | ||
@@ -58,0 +58,0 @@ "yargs": "^17.7.2", |
620934
14934
Updated@rjweb/utils@^1.12.20
Updatedinquirer@^9.2.23