Comparing version 0.3.0 to 0.4.0
/// <reference types="node" /> | ||
/// <reference types="node" /> | ||
import { Http2SecureServer, Http2Server } from 'node:http2'; | ||
import { Http2SecureServer, Http2Server, Http2ServerRequest, Http2ServerResponse } from 'node:http2'; | ||
import { Request } from './request.js'; | ||
import { Response } from './response.js'; | ||
import { Router } from './router.js'; | ||
import { Router, __InternalRouter } from './router.js'; | ||
export * from './middleware/index.js'; | ||
@@ -26,6 +26,12 @@ export declare const NEXT = "next"; | ||
} | ||
export interface __InternalApplication extends Application, __InternalRouter { | ||
server: Http2Server | Http2SecureServer; | ||
mainHandler(req: Http2ServerRequest, res: Http2ServerResponse): Promise<void>; | ||
invokedOptions: ApplicationOptions; | ||
} | ||
/** Similar to Express.Application */ | ||
export interface Application extends Router { | ||
(req: Http2ServerRequest, res: Http2ServerResponse): Promise<void>; | ||
listen(port: number, callback?: () => void): void; | ||
} | ||
export { Router, Request, Response, }; |
import { createSecureServer, createServer, } from 'node:http2'; | ||
import { readFileSync } from 'node:fs'; | ||
import { wrapRequest } from './request.js'; | ||
import { wrapResponse } from './response.js'; | ||
import { FluvialRequest } from './request.js'; | ||
import { FluvialResponse } from './response.js'; | ||
import { Router, routerPrototype } from './router.js'; | ||
@@ -10,21 +10,4 @@ export * from './middleware/index.js'; | ||
export function fluvial(options = {}) { | ||
let appInSettingStage = true; | ||
const app = new Proxy(Object.create(applicationPrototype), { | ||
apply(target, thisArg, argumentsArray) { | ||
target.mainHandler.apply(thisArg, argumentsArray); | ||
}, | ||
get(target, property) { | ||
if (property == 'apply') { | ||
return target.mainHandler.apply.bind(target.mainHandler); | ||
} | ||
return Reflect.get(target, property); | ||
}, | ||
set(target, property, value, receiver) { | ||
if ((property != 'server' && !appInSettingStage) || (property == 'server' && target.server)) { | ||
return false; | ||
} | ||
target[property] = value; | ||
return true; | ||
}, | ||
}); | ||
const app = Application; | ||
Reflect.setPrototypeOf(app, applicationPrototype); | ||
app.routes = []; | ||
@@ -37,9 +20,8 @@ app.mainHandler = Application; | ||
} | ||
appInSettingStage = false; | ||
return app; | ||
async function Application(rawRequest, rawResponse) { | ||
const req = wrapRequest(rawRequest); | ||
const res = wrapResponse(rawResponse); | ||
Object.defineProperty(req, 'response', res); | ||
Object.defineProperty(res, 'request', req); | ||
const req = new FluvialRequest(rawRequest); | ||
const res = new FluvialResponse(rawResponse); | ||
req.response = res; | ||
res.request = req; | ||
try { | ||
@@ -46,0 +28,0 @@ const result = await app.handleRequest(req.path, '/', req, res); |
@@ -44,3 +44,3 @@ export function deserializeUrlencodedPayload( /* options?: DeserializeUrlencodedOptions */) { | ||
} | ||
if (!subKey || subKey < 0) { | ||
if (!subKey || Number(subKey) < 0) { | ||
subKey = -1; | ||
@@ -47,0 +47,0 @@ } |
/// <reference types="node" /> | ||
/// <reference types="node" /> | ||
/// <reference types="node" /> | ||
/// <reference types="node" /> | ||
import { IncomingMessage } from 'node:http'; | ||
import { Http2ServerRequest, IncomingHttpHeaders } from 'node:http2'; | ||
import { Readable } from 'node:stream'; | ||
import { URL } from 'node:url'; | ||
import { ParamsDictionary, PathString, QueryDictionary } from './path-matching.js'; | ||
@@ -10,3 +14,3 @@ import { Response } from './response.js'; | ||
namespace Fluvial { | ||
interface BaseRequest { | ||
interface BaseRequest extends Readable { | ||
readonly response: Response; | ||
@@ -23,2 +27,5 @@ readonly path: PathString; | ||
} | ||
interface __InternalRequest extends BaseRequest { | ||
response: Response; | ||
} | ||
interface Http1Request extends BaseRequest { | ||
@@ -32,5 +39,23 @@ readonly rawRequest: IncomingMessage; | ||
} | ||
type Request = Http1Request | Http2Request; | ||
} | ||
} | ||
export declare function wrapRequest(rawRequest: Http2ServerRequest | IncomingMessage): Request; | ||
export type Request = Fluvial.Http1Request | Fluvial.Http2Request; | ||
export declare class FluvialRequest extends Readable implements Fluvial.BaseRequest { | ||
#private; | ||
rawRequest: IncomingMessage | Http2ServerRequest; | ||
get _parsedOriginalUrl(): URL; | ||
set _parsedOriginalUrl(value: URL); | ||
get params(): {}; | ||
get path(): `/${string}`; | ||
get query(): Readonly<Record<string, string>>; | ||
get httpVersion(): "1.1" | "2.0"; | ||
get headers(): Readonly<Record<string, string>>; | ||
get method(): SupportedHttpMethods; | ||
get hash(): string; | ||
set response(value: Response); | ||
get response(): Response; | ||
get payload(): any; | ||
constructor(rawRequest: IncomingMessage | Http2ServerRequest); | ||
_read(): Promise<void>; | ||
} | ||
export type Request = Fluvial.Request; |
import { constants } from 'node:http2'; | ||
export function wrapRequest(rawRequest) { | ||
const req = Object.create(requestPrototype); | ||
const rawPath = rawRequest.httpVersion == '1.1' ? | ||
rawRequest.url : | ||
rawRequest.headers[constants.HTTP2_HEADER_PATH]; | ||
const url = new URL(`http://foo.com${rawPath}`); | ||
Object.defineProperty(req, 'rawRequest', { get() { return rawRequest; } }); | ||
Object.defineProperty(req, 'path', { get() { return url.pathname; } }); | ||
// this gets filled later through path matching | ||
const params = {}; | ||
const query = Object.freeze(Object.fromEntries(url.searchParams.entries())); | ||
const headers = Object.freeze(Object.assign({}, rawRequest.headers)); | ||
Object.defineProperty(req, 'params', { get() { return params; } }); | ||
Object.defineProperty(req, 'query', { get() { return query; } }); | ||
Object.defineProperty(req, 'httpVersion', { get() { return rawRequest.httpVersion; } }); | ||
Object.defineProperty(req, 'headers', { get() { return headers; } }); | ||
Object.defineProperty(req, 'method', { get() { return rawRequest.method; } }); | ||
Object.defineProperty(req, 'hash', { get() { return url.hash; } }); | ||
return req; | ||
import { Readable } from 'node:stream'; | ||
import { URL } from 'node:url'; | ||
export class FluvialRequest extends Readable { | ||
rawRequest; | ||
get _parsedOriginalUrl() { | ||
return new URL(this.#url.href); | ||
} | ||
set _parsedOriginalUrl(value) { } | ||
get params() { | ||
return this.#params; | ||
} | ||
get path() { | ||
return this.#url.pathname; | ||
} | ||
get query() { | ||
return this.#query; | ||
} | ||
get httpVersion() { | ||
return this.rawRequest.httpVersion; | ||
} | ||
get headers() { | ||
return this.#headers; | ||
} | ||
get method() { | ||
return this.rawRequest.method; | ||
} | ||
get hash() { | ||
return this.#url.hash; | ||
} | ||
set response(value) { | ||
if (!this.#response) { | ||
this.#response = value; | ||
} | ||
} | ||
get response() { | ||
return this.#response; | ||
} | ||
get payload() { | ||
return this.#payload; | ||
} | ||
#params = {}; | ||
#url; | ||
#query; | ||
#headers; | ||
#response; | ||
#payload; | ||
constructor(rawRequest) { | ||
super(); | ||
this.rawRequest = rawRequest; | ||
const rawPath = rawRequest.httpVersion == '1.1' ? | ||
rawRequest.url : | ||
rawRequest.headers[constants.HTTP2_HEADER_PATH]; | ||
this.#url = new URL(`http://foo.com${rawPath}`); | ||
this.#query = Object.freeze(Object.fromEntries(this.#url.searchParams.entries())); | ||
this.#headers = Object.freeze(Object.assign({}, rawRequest.headers)); | ||
} | ||
async *#read() { | ||
try { | ||
for await (const chunk of this.rawRequest) { | ||
yield chunk; | ||
} | ||
} | ||
catch (e) { | ||
console.error(e); | ||
} | ||
} | ||
#readIterator; | ||
#readBuffer; | ||
async _read() { | ||
if (!this.#readIterator) { | ||
this.#readIterator = this.#read(); | ||
} | ||
if (this.#readBuffer && this.push(this.#readBuffer)) { | ||
this.#readBuffer = null; | ||
return; | ||
} | ||
try { | ||
const result = await this.#readIterator.next(); | ||
if (!result.value) { | ||
this.push(null); | ||
return; | ||
} | ||
const pushed = this.push(result.value); | ||
if (!pushed) { | ||
this.#readBuffer = result.value; | ||
} | ||
} | ||
catch (e) { | ||
console.error(e); | ||
} | ||
} | ||
} | ||
// if there are any methods that work best from the prototype, it should be added here | ||
const requestPrototype = {}; | ||
//# sourceMappingURL=request.js.map |
@@ -7,7 +7,6 @@ /// <reference types="node" /> | ||
import { Http2ServerResponse } from 'node:http2'; | ||
import { Readable } from 'node:stream'; | ||
import { Request } from './request'; | ||
import { Readable, Writable } from 'node:stream'; | ||
declare global { | ||
namespace Fluvial { | ||
interface BaseResponse { | ||
interface BaseResponse extends Writable { | ||
readonly request: Request; | ||
@@ -28,20 +27,13 @@ readonly rawResponse: Http2ServerResponse | ServerResponse; | ||
asEventSource(bool: boolean): this; | ||
/** pass-thru for http2Stream.write */ | ||
write(data: string | Buffer): void; | ||
/** DEFAULT MODE ONLY: the simplest way to respond with or without data */ | ||
send(data?: any): void; | ||
send(data?: any): Promise<this>; | ||
/** ideal for object data to be consumed in a browser */ | ||
json(data: object): void; | ||
/** DEFAULT MODE ONLY: pass-thru for http2Stream.end */ | ||
end(data?: string | Buffer): void; | ||
json(data: object): Promise<this>; | ||
/** EVENT STREAM MODE ONLY: when the response is set as an event stream, this sends the event; errors otherwise */ | ||
sendEvent(data?: object | string): this; | ||
/** ideal for needing to respond with files */ | ||
stream(stream: Readable): void; | ||
stream(stream: Readable): this; | ||
} | ||
interface __InternalResponse extends Fluvial.BaseResponse { | ||
_status: number; | ||
_eventSource: boolean; | ||
_eventSourceId: string; | ||
_send(data?: string): void; | ||
request: Request; | ||
} | ||
@@ -58,3 +50,23 @@ interface Http1Response extends BaseResponse { | ||
} | ||
export declare function wrapResponse(rawResponse: Http2ServerResponse | ServerResponse): Response; | ||
export declare class FluvialResponse extends Writable { | ||
#private; | ||
get httpVersion(): "1.1" | "2.0"; | ||
get component(): string; | ||
get responseSent(): boolean; | ||
headers: Record<string, string | string[]>; | ||
readonly rawResponse: Http2ServerResponse | ServerResponse; | ||
get request(): Fluvial.Request; | ||
set request(value: Fluvial.Request); | ||
constructor(rawResponse: Http2ServerResponse | ServerResponse); | ||
status(statusCode: number): this; | ||
status(): number; | ||
asEventSource(value: boolean): this; | ||
asEventSource(): boolean; | ||
_write(data: string | Buffer, encoding?: BufferEncoding): boolean; | ||
end(data?: string | Buffer | (() => void)): this; | ||
send(data?: string | object | Readable): Promise<this>; | ||
sendEvent(event: string | object): this; | ||
json(data?: object): Promise<this>; | ||
stream(sourceStream: Readable): this; | ||
} | ||
export type Response = Fluvial.Http1Response | Fluvial.Http2Response; |
import { randomUUID } from 'node:crypto'; | ||
import { constants } from 'node:http2'; | ||
import { Readable } from 'node:stream'; | ||
export function wrapResponse(rawResponse) { | ||
const res = Object.create(responsePrototype); | ||
const headers = new Proxy({}, { | ||
get(target, property) { | ||
import { Readable, Writable } from 'node:stream'; | ||
import { finished } from 'node:stream/promises'; | ||
export class FluvialResponse extends Writable { | ||
get httpVersion() { | ||
return this.request.httpVersion; | ||
} | ||
get component() { | ||
return 'response'; | ||
} | ||
get responseSent() { | ||
return this.httpVersion == '1.1' ? | ||
this.rawResponse.headersSent : | ||
this.rawResponse.stream.headersSent; | ||
} | ||
headers = new Proxy({}, { | ||
get: (target, property) => { | ||
if (typeof property == 'symbol') { | ||
return target[property]; | ||
} | ||
return target[property.toLowerCase()]; | ||
}, | ||
set(target, property, newValue) { | ||
set: (target, property, newValue) => { | ||
const lowerProperty = property.toLowerCase(); | ||
if (!newValue && rawResponse.hasHeader(lowerProperty)) { | ||
rawResponse.removeHeader(lowerProperty); | ||
if (!newValue && this.rawResponse.hasHeader(lowerProperty)) { | ||
this.rawResponse.removeHeader(lowerProperty); | ||
delete target[lowerProperty]; | ||
} | ||
else if (newValue) { | ||
rawResponse.setHeader(lowerProperty, newValue); | ||
target[lowerProperty] = rawResponse.getHeader(lowerProperty); | ||
this.rawResponse.setHeader(lowerProperty, newValue); | ||
target[lowerProperty] = this.rawResponse.getHeader(lowerProperty); | ||
} | ||
return true; | ||
}, | ||
ownKeys(target) { | ||
ownKeys: (target) => { | ||
return Object.keys(target); | ||
}, | ||
has(target, p) { | ||
return rawResponse.hasHeader(p) ?? p.toLowerCase() in target; | ||
has: (target, p) => { | ||
return this.rawResponse.hasHeader(p) ?? p.toLowerCase() in target; | ||
}, | ||
deleteProperty(target, p) { | ||
deleteProperty: (target, p) => { | ||
const lowerProperty = p.toLowerCase(); | ||
if (rawResponse.hasHeader(lowerProperty)) { | ||
rawResponse.removeHeader(lowerProperty); | ||
if (this.rawResponse.hasHeader(lowerProperty)) { | ||
this.rawResponse.removeHeader(lowerProperty); | ||
} | ||
@@ -36,26 +50,21 @@ return delete target[lowerProperty]; | ||
}); | ||
Object.defineProperty(res, 'httpVersion', { get() { return rawResponse.req.httpVersion; } }); | ||
Object.defineProperty(res, 'rawResponse', { get() { return rawResponse; } }); | ||
Object.defineProperty(res, 'headers', { | ||
value: headers, | ||
writable: false, | ||
enumerable: true, | ||
}); | ||
res._status = 200; | ||
res._eventSource = false; | ||
return res; | ||
} | ||
const responsePrototype = { | ||
get component() { | ||
return 'response'; | ||
}, | ||
get responseSent() { | ||
const self = this; | ||
return self.httpVersion == '1.1' ? | ||
self.rawResponse.headersSent : | ||
self.rawResponse.stream.headersSent; | ||
}, | ||
rawResponse; | ||
#req; | ||
get request() { | ||
return this.#req; | ||
} | ||
set request(value) { | ||
if (!this.#req) { | ||
this.#req = value; | ||
} | ||
} | ||
#status = 200; | ||
#eventSource = false; | ||
constructor(rawResponse) { | ||
super(); | ||
this.rawResponse = rawResponse; | ||
} | ||
status(statusCode) { | ||
if (!statusCode) { | ||
return this._status; | ||
return this.#status; | ||
} | ||
@@ -65,16 +74,20 @@ if (this.rawResponse.headersSent) { | ||
} | ||
this._status = statusCode; | ||
}, | ||
this.rawResponse.statusCode = statusCode; | ||
this.#status = statusCode; | ||
return this; | ||
} | ||
asEventSource(value) { | ||
if (typeof value != 'boolean') { | ||
return this._eventSource; | ||
if (value == undefined) { | ||
return this.#eventSource; | ||
} | ||
if (value) { | ||
this._eventSourceId = randomUUID(); | ||
// "connection" is only necessary for http/1.x responses and are ignored by Node for http/2 responses | ||
this.headers['connection'] = 'keep-alive'; | ||
this.#eventSourceId = randomUUID(); | ||
// "connection" is only necessary for http/1.x responses and is ignored with a warning by Node for http/2 responses | ||
if (this.httpVersion == '1.1') { | ||
this.headers['connection'] = 'keep-alive'; | ||
} | ||
this.headers['content-type'] = 'text/event-stream'; | ||
this.headers['cache-control'] = 'no-cache'; | ||
} | ||
else if (this._eventSourceId) { | ||
else if (this.#eventSourceId) { | ||
if (this.headers['connection'] == 'keep-alive') { | ||
@@ -86,17 +99,21 @@ this.headers['connection'] = undefined; | ||
} | ||
this._eventSourceId = null; | ||
this.#eventSourceId = null; | ||
} | ||
this._eventSource = value; | ||
}, | ||
write(data) { | ||
this.rawResponse.write(data); | ||
}, | ||
this.#eventSource = value; | ||
return this; | ||
} | ||
#eventSourceId; | ||
_write(data, encoding = 'utf-8') { | ||
return this.rawResponse.write(data, encoding); | ||
} | ||
end(data) { | ||
if (this._eventSource) { | ||
if (this.#eventSource) { | ||
throw TypeError('An attempt to close the response stream failed because the response is set up to be an event source and only clients are allowed to close such streams'); | ||
} | ||
// all overloads of writable.end will work here... | ||
this.rawResponse.end(data); | ||
}, | ||
return this; | ||
} | ||
send(data) { | ||
if (this._eventSource) { | ||
if (this.#eventSource) { | ||
throw TypeError('An attempt to send a singular response failed because the response is set up to be an event source; use sendEvent instead'); | ||
@@ -118,6 +135,6 @@ } | ||
} | ||
this._send(data); | ||
}, | ||
return this.#send(data); | ||
} | ||
sendEvent(event) { | ||
if (!this._eventSource) { | ||
if (!this.#eventSource) { | ||
throw TypeError('An attempt to send an event failed because this response is not set up to be an event source; must use the asEventSource() setter first'); | ||
@@ -130,3 +147,3 @@ } | ||
if (this.httpVersion == '1.1') { | ||
this.rawResponse.writeHead(this._status, { | ||
this.rawResponse.writeHead(this.#status, { | ||
...this.headers | ||
@@ -137,3 +154,3 @@ }); | ||
this.rawResponse.stream.respond({ | ||
[constants.HTTP2_HEADER_STATUS]: this._status, | ||
[constants.HTTP2_HEADER_STATUS]: this.#status, | ||
...this.headers, | ||
@@ -144,5 +161,6 @@ }); | ||
const preparedData = typeof event == 'string' ? event : JSON.stringify(event); | ||
this.rawResponse.write('id: ' + this._eventSourceId + '\n' + | ||
this.rawResponse.write('id: ' + this.#eventSourceId + '\n' + | ||
'data: ' + preparedData + '\n\n'); | ||
}, | ||
return this; | ||
} | ||
json(data) { | ||
@@ -156,5 +174,5 @@ if (data && typeof data != 'object') { | ||
} | ||
this._send(stringifiedData); | ||
}, | ||
async stream(sourceStream) { | ||
return this.#send(stringifiedData); | ||
} | ||
stream(sourceStream) { | ||
if (this.responseSent) { | ||
@@ -164,3 +182,3 @@ throw TypeError('attempted to send another response though the response stream is closed'); | ||
if (this.httpVersion == '1.1') { | ||
this.rawResponse.writeHead(this._status); | ||
this.rawResponse.writeHead(this.#status); | ||
} | ||
@@ -170,10 +188,11 @@ else { | ||
...this.headers, | ||
[constants.HTTP2_HEADER_STATUS]: this._status, | ||
[constants.HTTP2_HEADER_STATUS]: this.#status, | ||
}); | ||
} | ||
sourceStream.pipe(this.rawResponse); | ||
}, | ||
_send(data) { | ||
return this; | ||
} | ||
async #send(data) { | ||
if (this.httpVersion == '1.1') { | ||
this.rawResponse.writeHead(this._status, { ...this.headers }); | ||
this.rawResponse.writeHead(this.#status, { ...this.headers }); | ||
} | ||
@@ -183,11 +202,14 @@ else { | ||
...this.headers, | ||
[constants.HTTP2_HEADER_STATUS]: this._status, | ||
[constants.HTTP2_HEADER_STATUS]: this.#status, | ||
}); | ||
} | ||
if (data) { | ||
this.rawResponse.write(Buffer.from(data)); | ||
this.write(Buffer.from(data)); | ||
} | ||
this.rawResponse.end(); | ||
}, | ||
}; | ||
this.end(); | ||
await finished(this.rawResponse); | ||
return this; | ||
} | ||
} | ||
; | ||
//# sourceMappingURL=response.js.map |
@@ -8,3 +8,3 @@ import { type PathString, type PathMatcher } from './path-matching.js'; | ||
interface __InternalRouter extends Router { | ||
handleRequest(remainingPath: PathString, matchedPath: string, req: Request, res: Response, err?: unknown, queryAssigned?: boolean): Promise<void | 'next'>; | ||
handleRequest(remainingPath: PathString, matchedPath: string, req: Request, res: Response, err?: unknown): Promise<void | 'next'>; | ||
__getMatchingRoute(state: __RouterState): Generator<__InternalRoute>; | ||
@@ -32,3 +32,3 @@ routes: __InternalRoute[]; | ||
interface __InternalRoute extends Route { | ||
handleRequest(remainingPath: string, matchedPath: string, req: Request, res: Response, err?: unknown, queryAssigned?: boolean): Promise<void | 'next'>; | ||
handleRequest(remainingPath: string, matchedPath: string, req: Request, res: Response, err?: unknown): Promise<void | 'next'>; | ||
__getMatchingHandlers(this: __InternalRoute, state: __RouteHandlerState): Generator<(RequestHandler | ErrorHandler | __InternalRouter)[]>; | ||
@@ -46,3 +46,2 @@ handlers: [method: HandlerHttpMethods, ...handlers: (RequestHandler | ErrorHandler | Router)[]][]; | ||
end?: boolean; | ||
queryAssigned?: boolean; | ||
} | ||
@@ -55,3 +54,2 @@ interface __RouteHandlerState { | ||
end?: boolean; | ||
queryAssigned?: boolean; | ||
} | ||
@@ -101,3 +99,3 @@ interface Route { | ||
*/ | ||
handleRequest(this: __InternalRouter, path: PathString, matchedPath: PathString, req: Request, res: Response, err?: unknown, queryAssigned?: boolean): Promise<void | 'next'>; | ||
handleRequest(this: __InternalRouter, path: PathString, matchedPath: PathString, req: Request, res: Response, err?: unknown): Promise<void | 'next'>; | ||
__getMatchingRoute: (state: __RouterState) => Generator<__InternalRoute>; | ||
@@ -104,0 +102,0 @@ __addRoute(this: __InternalRouter, method: HandlerHttpMethods | null, path: PathMatcher, ...handlers: (RequestHandler | ErrorHandler | Router)[]): Fluvial.__InternalRoute; |
@@ -1,2 +0,2 @@ | ||
import { getRouteParams, getQueryParams, } from './path-matching.js'; | ||
import { getRouteParams, } from './path-matching.js'; | ||
export function Router() { | ||
@@ -68,3 +68,3 @@ const router = Object.create(routerPrototype); | ||
*/ | ||
async handleRequest(path, matchedPath, req, res, err, queryAssigned = false) { | ||
async handleRequest(path, matchedPath, req, res, err) { | ||
// by default, if there doesn't happen to be any handlers that work with this request, it should direct it to the next handler found | ||
@@ -77,7 +77,6 @@ let latestResult = 'next'; | ||
matchedPath, | ||
queryAssigned, | ||
}; | ||
for (const route of this.__getMatchingRoute(routerState)) { | ||
try { | ||
latestResult = await route.handleRequest(path, matchedPath, req, res, routerState.error, routerState.queryAssigned); | ||
latestResult = await route.handleRequest(path, matchedPath, req, res, routerState.error); | ||
if (routerState.error) { | ||
@@ -109,8 +108,2 @@ // consider the error handled and resolve as if not errored | ||
const params = getRouteParams(state.path, route.pathMatcher); | ||
if (!state.queryAssigned) { | ||
// TODO: put this at the beginning of the request cycle; the assignment here is not great... | ||
const query = getQueryParams(state.path); | ||
Object.assign(state.req.query, query); | ||
state.queryAssigned = true; | ||
} | ||
if (params) { | ||
@@ -140,3 +133,3 @@ Object.assign(state.req.params, params); | ||
*/ | ||
async handleRequest(path, matchedPath, req, res, err, queryAssigned = false) { | ||
async handleRequest(path, matchedPath, req, res, err) { | ||
// by default, if there doesn't happen to be any handlers that work with this request, it should direct it to the next handler found | ||
@@ -149,3 +142,2 @@ let latestResult = 'next'; | ||
matchedPath, | ||
queryAssigned, | ||
}; | ||
@@ -172,3 +164,3 @@ for (const handlers of this.__getMatchingHandlers(handlerState)) { | ||
(matchedPath == '/' ? '' : matchedPath) + path.slice(0, path.indexOf(remainingPath)); | ||
latestResult = await handler.handleRequest(remainingPath, previousPath, req, res, handlerState.error, handlerState.queryAssigned); | ||
latestResult = await handler.handleRequest(remainingPath, previousPath, req, res, handlerState.error); | ||
} | ||
@@ -175,0 +167,0 @@ if (latestResult != 'next') { |
{ | ||
"name": "fluvial", | ||
"version": "0.3.0", | ||
"version": "0.4.0", | ||
"description": "Fluvial: A light http/2 server framework, similar to Express", | ||
@@ -31,5 +31,4 @@ "main": "dist/index.js", | ||
"devDependencies": { | ||
"@types/express": "^4.17.16", | ||
"@types/mime": "^3.0.1", | ||
"@types/node": "^18.11.18", | ||
"@types/node": "^20.4.1", | ||
"@vitest/coverage-c8": "^0.28.1", | ||
@@ -39,3 +38,3 @@ "pnpm": "^7.26.0", | ||
"ts-node": "^10.9.1", | ||
"typescript": "^4.9.4", | ||
"typescript": "^5.1.6", | ||
"vite": "^4.0.4", | ||
@@ -48,3 +47,3 @@ "vitest": "^0.28.1" | ||
"scripts": { | ||
"compile": "tsc", | ||
"compile": "tsc -b", | ||
"test": "vitest", | ||
@@ -51,0 +50,0 @@ "test:coverage": "pnpm test --coverage", |
@@ -53,2 +53,23 @@ # Fluvial | ||
### Quick note about SSL | ||
In an ideal environment, you will want to have SSL that is handled before the request is handed off to your fluvial application (e.g., load balancers, possibly nginx or Apache, etc.). However, if you want to develop with HTTP/2 locally, browsers will not connect to servers with HTTP/2 without the connection being made over SSL. | ||
Therefore, a quick-n-dirty solution is to generate a local SSL certificate and providing it like this: | ||
```sh | ||
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365 -nodes | ||
``` | ||
```js | ||
const app = fluvial({ | ||
ssl: { | ||
certificatePath: path.join('path', 'to', 'cert.pem'), | ||
keyPath: path.join('path', 'to', 'key.pem'), | ||
}, | ||
}); | ||
``` | ||
To ensure this only applies locally, you can set an environment variable that your code can detect and then apply the ssl configuration. | ||
## API Documentation | ||
@@ -55,0 +76,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
116730
9
50
1513
349