Comparing version 0.2.4 to 0.2.5
@@ -202,19 +202,20 @@ "use strict"; | ||
// handle fallback, incase command arg not present | ||
const command = this.find(commandarg.arg); | ||
if (command) { | ||
const composed = command.compose(); | ||
const ctx = new CliContext(exec, script, composed); | ||
processCommand(ctx, parsed); | ||
ctx.set('command', command); | ||
if (this.asyncLocalStorage) { | ||
return await this.asyncLocalStorage.run(ctx, async () => { | ||
await composedMiddleware(ctx, async () => { | ||
await command.handle(ctx); | ||
}); | ||
const command = this.find(commandarg.arg) || this._fallback; | ||
if (!command) { | ||
throw new Error(`invalid command ${command}`); | ||
} | ||
const composed = command.compose(); | ||
const ctx = new CliContext(exec, script, composed); | ||
processCommand(ctx, parsed); | ||
ctx.set('command', command); | ||
if (this.asyncLocalStorage) { | ||
return await this.asyncLocalStorage.run(ctx, async () => { | ||
await composedMiddleware(ctx, async () => { | ||
await command.handle(ctx); | ||
}); | ||
} | ||
await composedMiddleware(ctx, async () => { | ||
await command.handle(ctx); | ||
}); | ||
} | ||
await composedMiddleware(ctx, async () => { | ||
await command.handle(ctx); | ||
}); | ||
} | ||
@@ -221,0 +222,0 @@ } |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.VERSION = void 0; | ||
exports.VERSION = "0.2.1"; | ||
exports.VERSION = "0.2.4"; | ||
//# sourceMappingURL=consts.js.map |
@@ -28,3 +28,3 @@ "use strict"; | ||
const cors = new HttpCors(opts); | ||
return cors.handle; | ||
return cors.handle.bind(this); | ||
} | ||
@@ -31,0 +31,0 @@ /** |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.HttpAsset = void 0; | ||
exports.HttpAssetMiddleware = exports.HttpAsset = void 0; | ||
const promises_1 = require("node:fs/promises"); | ||
@@ -32,3 +32,3 @@ const node_fs_1 = require("node:fs"); | ||
const handler = async (ctx) => { | ||
await this.file(ctx, { path: ctx.path }, ctx.method.toLowerCase() == 'head'); | ||
await this.file(ctx, { path: ctx.path }); | ||
}; | ||
@@ -83,3 +83,4 @@ const route = new index_js_1.HttpRouter(options); | ||
} | ||
async file(ctx, file, head = false) { | ||
async file(ctx, file, head) { | ||
head = head || ctx.method.toLowerCase() == 'head'; | ||
const absfile = path.join(this.opts.root, file.path); | ||
@@ -112,3 +113,9 @@ const info = await this.info(absfile); | ||
}); | ||
return; | ||
} | ||
ctx.reply({ | ||
status: 200, | ||
body: '', | ||
headers: { 'content-type': file.mime || info.mime }, | ||
}); | ||
} | ||
@@ -130,2 +137,105 @@ css(ctx, file) { | ||
exports.HttpAsset = HttpAsset; | ||
class HttpAssetMiddleware { | ||
opts; | ||
store = {}; | ||
maxAgeSeconds = 60; // 200*24*60*60 | ||
constructor(opts) { | ||
this.opts = opts; | ||
} | ||
static middleware(opts) { | ||
const cors = new HttpAssetMiddleware(opts); | ||
return cors.handle.bind(this); | ||
} | ||
async handle(ctx, next) { | ||
await next(); | ||
if (ctx.response.status == 404) { | ||
this.file(ctx, { path: ctx.path }); | ||
} | ||
} | ||
/** | ||
* caution: returns ref to object | ||
* @param file | ||
* @returns | ||
*/ | ||
async info(file) { | ||
if (this.store[file]) { | ||
return this.store[file]; | ||
} | ||
try { | ||
const info = await (0, promises_1.stat)(file); | ||
const mime = (0, mime_js_1.GuessMime)(file); | ||
return this.store[file] = { | ||
size: info.size, | ||
modified: info.mtime, | ||
checksome: await (0, file_js_1.checksum)(file), | ||
mime, | ||
}; | ||
} | ||
catch (_) { | ||
return null; | ||
} | ||
} | ||
streamOptions(ctx) { | ||
const options = {}; | ||
//if (this.opts.allowRange) { | ||
const range = ctx.headers.get('range'); | ||
if (range && range.startsWith('bytes=')) { | ||
const [positions] = range.replace(/bytes=/, '').split('-'); | ||
options.start = parseInt(positions[0], 10); | ||
if (positions[1]) { | ||
const end = parseInt(positions[1], 10); | ||
if (end && !Number.isNaN(end)) { | ||
options.end = end; | ||
} | ||
} | ||
} | ||
//} | ||
return options; | ||
} | ||
async file(ctx, file, head) { | ||
head = head || ctx.method.toLowerCase() == 'head'; | ||
const absfile = path.join(this.opts.root, file.path); | ||
const info = await this.info(absfile); | ||
if (!info) { | ||
ctx.abort(404); | ||
return; | ||
} | ||
if (!this.opts.debug) { | ||
// content length may vary in debug mode | ||
ctx.headers.set('Content-Length', info.size.toString()); | ||
ctx.headers.set('ETag', info.checksome); | ||
} | ||
if (this.opts.cache && !this.opts.debug) { | ||
// only set cache-control, we cache enabled and not in debug mode | ||
const etag = ctx.headers.get('if-none-match'); | ||
if (etag && etag === info.checksome) { | ||
ctx.abort(304); | ||
return; | ||
} | ||
ctx.headers.set('Cache-Control', `public, max-age=${this.maxAgeSeconds}`); | ||
} | ||
if (!head) { | ||
const options = this.streamOptions(ctx); | ||
try { | ||
// TODO: prune from info incase file is not available | ||
const body = (0, node_fs_1.createReadStream)(absfile, options); | ||
ctx.reply({ | ||
status: options.start ? 206 : 200, | ||
body, | ||
headers: { 'content-type': file.mime || info.mime }, | ||
}); | ||
} | ||
catch { | ||
delete this.store[absfile]; | ||
} | ||
return; | ||
} | ||
ctx.reply({ | ||
status: 200, | ||
body: '', | ||
headers: { 'content-type': file.mime || info.mime }, | ||
}); | ||
} | ||
} | ||
exports.HttpAssetMiddleware = HttpAssetMiddleware; | ||
//# sourceMappingURL=asset.js.map |
@@ -22,4 +22,6 @@ "use strict"; | ||
__exportStar(require("./mime.js"), exports); | ||
__exportStar(require("./path.js"), exports); | ||
__exportStar(require("./random.js"), exports); | ||
__exportStar(require("./result.js"), exports); | ||
__exportStar(require("./type.js"), exports); | ||
//# sourceMappingURL=index.js.map |
@@ -198,19 +198,20 @@ import * as util from 'node:util'; | ||
// handle fallback, incase command arg not present | ||
const command = this.find(commandarg.arg); | ||
if (command) { | ||
const composed = command.compose(); | ||
const ctx = new CliContext(exec, script, composed); | ||
processCommand(ctx, parsed); | ||
ctx.set('command', command); | ||
if (this.asyncLocalStorage) { | ||
return await this.asyncLocalStorage.run(ctx, async () => { | ||
await composedMiddleware(ctx, async () => { | ||
await command.handle(ctx); | ||
}); | ||
const command = this.find(commandarg.arg) || this._fallback; | ||
if (!command) { | ||
throw new Error(`invalid command ${command}`); | ||
} | ||
const composed = command.compose(); | ||
const ctx = new CliContext(exec, script, composed); | ||
processCommand(ctx, parsed); | ||
ctx.set('command', command); | ||
if (this.asyncLocalStorage) { | ||
return await this.asyncLocalStorage.run(ctx, async () => { | ||
await composedMiddleware(ctx, async () => { | ||
await command.handle(ctx); | ||
}); | ||
} | ||
await composedMiddleware(ctx, async () => { | ||
await command.handle(ctx); | ||
}); | ||
} | ||
await composedMiddleware(ctx, async () => { | ||
await command.handle(ctx); | ||
}); | ||
} | ||
@@ -217,0 +218,0 @@ } |
@@ -1,2 +0,2 @@ | ||
export const VERSION = "0.2.1"; | ||
export const VERSION = "0.2.4"; | ||
//# sourceMappingURL=consts.js.map |
@@ -25,3 +25,3 @@ const defaultOptions = { | ||
const cors = new HttpCors(opts); | ||
return cors.handle; | ||
return cors.handle.bind(this); | ||
} | ||
@@ -28,0 +28,0 @@ /** |
@@ -29,3 +29,3 @@ import { stat, writeFile } from 'node:fs/promises'; | ||
const handler = async (ctx) => { | ||
await this.file(ctx, { path: ctx.path }, ctx.method.toLowerCase() == 'head'); | ||
await this.file(ctx, { path: ctx.path }); | ||
}; | ||
@@ -80,3 +80,4 @@ const route = new HttpRouter(options); | ||
} | ||
async file(ctx, file, head = false) { | ||
async file(ctx, file, head) { | ||
head = head || ctx.method.toLowerCase() == 'head'; | ||
const absfile = path.join(this.opts.root, file.path); | ||
@@ -109,3 +110,9 @@ const info = await this.info(absfile); | ||
}); | ||
return; | ||
} | ||
ctx.reply({ | ||
status: 200, | ||
body: '', | ||
headers: { 'content-type': file.mime || info.mime }, | ||
}); | ||
} | ||
@@ -126,2 +133,104 @@ css(ctx, file) { | ||
} | ||
export class HttpAssetMiddleware { | ||
opts; | ||
store = {}; | ||
maxAgeSeconds = 60; // 200*24*60*60 | ||
constructor(opts) { | ||
this.opts = opts; | ||
} | ||
static middleware(opts) { | ||
const cors = new HttpAssetMiddleware(opts); | ||
return cors.handle.bind(this); | ||
} | ||
async handle(ctx, next) { | ||
await next(); | ||
if (ctx.response.status == 404) { | ||
this.file(ctx, { path: ctx.path }); | ||
} | ||
} | ||
/** | ||
* caution: returns ref to object | ||
* @param file | ||
* @returns | ||
*/ | ||
async info(file) { | ||
if (this.store[file]) { | ||
return this.store[file]; | ||
} | ||
try { | ||
const info = await stat(file); | ||
const mime = GuessMime(file); | ||
return this.store[file] = { | ||
size: info.size, | ||
modified: info.mtime, | ||
checksome: await checksum(file), | ||
mime, | ||
}; | ||
} | ||
catch (_) { | ||
return null; | ||
} | ||
} | ||
streamOptions(ctx) { | ||
const options = {}; | ||
//if (this.opts.allowRange) { | ||
const range = ctx.headers.get('range'); | ||
if (range && range.startsWith('bytes=')) { | ||
const [positions] = range.replace(/bytes=/, '').split('-'); | ||
options.start = parseInt(positions[0], 10); | ||
if (positions[1]) { | ||
const end = parseInt(positions[1], 10); | ||
if (end && !Number.isNaN(end)) { | ||
options.end = end; | ||
} | ||
} | ||
} | ||
//} | ||
return options; | ||
} | ||
async file(ctx, file, head) { | ||
head = head || ctx.method.toLowerCase() == 'head'; | ||
const absfile = path.join(this.opts.root, file.path); | ||
const info = await this.info(absfile); | ||
if (!info) { | ||
ctx.abort(404); | ||
return; | ||
} | ||
if (!this.opts.debug) { | ||
// content length may vary in debug mode | ||
ctx.headers.set('Content-Length', info.size.toString()); | ||
ctx.headers.set('ETag', info.checksome); | ||
} | ||
if (this.opts.cache && !this.opts.debug) { | ||
// only set cache-control, we cache enabled and not in debug mode | ||
const etag = ctx.headers.get('if-none-match'); | ||
if (etag && etag === info.checksome) { | ||
ctx.abort(304); | ||
return; | ||
} | ||
ctx.headers.set('Cache-Control', `public, max-age=${this.maxAgeSeconds}`); | ||
} | ||
if (!head) { | ||
const options = this.streamOptions(ctx); | ||
try { | ||
// TODO: prune from info incase file is not available | ||
const body = createReadStream(absfile, options); | ||
ctx.reply({ | ||
status: options.start ? 206 : 200, | ||
body, | ||
headers: { 'content-type': file.mime || info.mime }, | ||
}); | ||
} | ||
catch { | ||
delete this.store[absfile]; | ||
} | ||
return; | ||
} | ||
ctx.reply({ | ||
status: 200, | ||
body: '', | ||
headers: { 'content-type': file.mime || info.mime }, | ||
}); | ||
} | ||
} | ||
//# sourceMappingURL=asset.js.map |
@@ -6,4 +6,6 @@ export * from './asset.js'; | ||
export * from './mime.js'; | ||
export * from './path.js'; | ||
export * from './random.js'; | ||
export * from './result.js'; | ||
export * from './type.js'; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "astad", | ||
"version": "0.2.4", | ||
"version": "0.2.5", | ||
"description": "astad is a nodejs framework for api and web development", | ||
@@ -5,0 +5,0 @@ "main": "./cjs/index.js", |
@@ -44,1 +44,23 @@ import { IHttpContext } from "../http/context.js"; | ||
} | ||
export interface IHttpAssetMiddlewareOpts { | ||
root: string; | ||
debug: boolean; | ||
cache: boolean; | ||
maxAgeSeconds: number; | ||
} | ||
export declare class HttpAssetMiddleware { | ||
readonly opts: IHttpAssetMiddlewareOpts; | ||
store: Record<string, any>; | ||
maxAgeSeconds: number; | ||
constructor(opts: IHttpAssetMiddlewareOpts); | ||
static middleware(opts: IHttpAssetMiddlewareOpts): (ctx: IHttpContext, next: any) => Promise<void>; | ||
handle(ctx: IHttpContext, next: any): Promise<void>; | ||
/** | ||
* caution: returns ref to object | ||
* @param file | ||
* @returns | ||
*/ | ||
info(file: string): Promise<IFileinfo | null>; | ||
streamOptions(ctx: IHttpContext): any; | ||
file(ctx: IHttpContext, file: IHttpAssetFile, head?: boolean): Promise<void>; | ||
} |
@@ -6,3 +6,5 @@ export * from './asset.js'; | ||
export * from './mime.js'; | ||
export * from './path.js'; | ||
export * from './random.js'; | ||
export * from './result.js'; | ||
export * from './type.js'; |
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
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
421385
271
7237