@aomex/web
Advanced tools
Comparing version 1.0.4 to 1.1.0
@@ -1,55 +0,11 @@ | ||
# @aomex/web | ||
# Change Log | ||
## 1.0.4 | ||
All notable changes to this project will be documented in this file. | ||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. | ||
### Patch Changes | ||
# 1.1.0 (2024-06-27) | ||
- [`a1a1787`](https://github.com/aomex/aomex/commit/a1a1787c791bf113c7e87e4435ea364eb8ce8eb8) Thanks [@geekact](https://github.com/geekact)! - refactor: 使用node内置的 util.styleText 替换 chalk 包 | ||
- [`42447fb`](https://github.com/aomex/aomex/commit/42447fb9d3916c4d6242841d1fa3eab67e48be70) Thanks [@geekact](https://github.com/geekact)! - refactor(core): 重命名I18nFormat为I18nMessage | ||
### Features | ||
- [`170cb21`](https://github.com/aomex/aomex/commit/170cb214c9e4eb9de6ec388ba97fe06996c7f268) Thanks [@geekact](https://github.com/geekact)! - feat(web): 允许监听https服务 | ||
- Updated dependencies [[`a1a1787`](https://github.com/aomex/aomex/commit/a1a1787c791bf113c7e87e4435ea364eb8ce8eb8)]: | ||
- @aomex/internal-tools@1.0.4 | ||
## 1.0.3 | ||
### Patch Changes | ||
- [`5f09bc3`](https://github.com/aomex/aomex/commit/5f09bc3dda73bda9e69b5968bc913982d53dd4de) Thanks [@geekact](https://github.com/geekact)! - chore(web): 升级co-body包 6.1.0 -> 6.2.0 | ||
- [`48c803e`](https://github.com/aomex/aomex/commit/48c803e35700a4718f3c993e27bc982feb29061e) Thanks [@geekact](https://github.com/geekact)! - feat(web,router): 响应格式静态类型约束 | ||
- [`eb8b1eb`](https://github.com/aomex/aomex/commit/eb8b1ebdc7072d7ee9c7ba8e1f324e802753416d) Thanks [@geekact](https://github.com/geekact)! - feat(openapi): 文档生成工具 | ||
- Updated dependencies []: | ||
- @aomex/internal-tools@1.0.3 | ||
## 1.0.2 | ||
### Patch Changes | ||
- Updated dependencies []: | ||
- @aomex/internal-tools@1.0.2 | ||
## 1.0.1 | ||
### Patch Changes | ||
- [`5543b66`](https://github.com/aomex/aomex/commit/5543b66379e70d18feffec08e8101065ca00c541) Thanks [@geekact](https://github.com/geekact)! - fix(web): 在tsx下运行报错 | ||
- Updated dependencies []: | ||
- @aomex/internal-tools@1.0.1 | ||
## 1.0.0 | ||
### Minor Changes | ||
- [`11103a2`](https://github.com/aomex/aomex/commit/11103a2aff4e081754c56b0ff18fa5130ca252e8) Thanks [@geekact](https://github.com/geekact)! - 重新制作 | ||
### Patch Changes | ||
- Updated dependencies [[`11103a2`](https://github.com/aomex/aomex/commit/11103a2aff4e081754c56b0ff18fa5130ca252e8)]: | ||
- @aomex/core@1.0.0 | ||
- @aomex/internal-tools@1.0.0 | ||
* 初始化 ([3350159](https://github.com/aomex/aomex/commit/3350159454ad230e6d910405f907293b059b1f49)) |
@@ -32,2 +32,7 @@ import { I18nMessage, Next, Middleware, OpenAPI, MiddlewareChain, MixinMiddlewareToken, Validator, TransformedValidator, magistrate, MixinMiddlewareChain, I18n, ValidatorToken } from '@aomex/core'; | ||
}; | ||
response: { | ||
redirect: I18nMessage<{ | ||
url: string; | ||
}>; | ||
}; | ||
}; | ||
@@ -102,2 +107,3 @@ } | ||
type Body = string | object | Stream | Buffer | null; | ||
type BodyType = 'string' | 'json' | 'stream' | 'buffer' | 'null'; | ||
declare class WebResponse { | ||
@@ -111,2 +117,4 @@ app: WebApp; | ||
private _body; | ||
private _bodyType; | ||
private _jsonString; | ||
/** | ||
@@ -121,3 +129,3 @@ * 是否明确设置过内容 | ||
private _statusCode; | ||
private _updatingBodyType; | ||
private _updatingContentType; | ||
constructor(req: IncomingMessage); | ||
@@ -140,3 +148,3 @@ get contentLength(): number; | ||
hasHeader: (name: UpperStringHeaderKeys | UpperArrayHeaderKeys | (string & {})) => boolean; | ||
isJSON(body: Body): body is object; | ||
isJSON(value: Body): value is object; | ||
matchContentType(type: string, ...types: string[]): string | null; | ||
@@ -164,3 +172,4 @@ onError(error?: Error | HttpError | null): void; | ||
protected setStatus(code: number): void; | ||
protected updateBodyType(): void; | ||
protected updateContentType(): void; | ||
protected getBodyType(body: Body): BodyType; | ||
} | ||
@@ -514,2 +523,2 @@ | ||
export { type Body, FileValidator, type HttpErrorProperties, type OpenApiInjector, WebApp, type WebAppOption, WebContext, WebMiddleware, WebMiddlewareChain, type WebMiddlewareToken, WebRequest, WebResponse, WebResponseMiddleware, type WebResponseOptions, body, params, query, response }; | ||
export { type Body, type BodyType, FileValidator, type HttpErrorProperties, type OpenApiInjector, WebApp, type WebAppOption, WebContext, WebMiddleware, WebMiddlewareChain, type WebMiddlewareToken, WebRequest, WebResponse, WebResponseMiddleware, type WebResponseOptions, body, params, query, response }; |
@@ -10,2 +10,5 @@ // src/i18n/locales/zh-cn.ts | ||
} | ||
}, | ||
response: { | ||
redirect: "\u6B63\u5728\u91CD\u5B9A\u5411\u5230 {{url}}" | ||
} | ||
@@ -23,2 +26,5 @@ }); | ||
} | ||
}, | ||
response: { | ||
redirect: "Redirecting to {{url}}" | ||
} | ||
@@ -40,5 +46,5 @@ }); | ||
import fresh from "fresh"; | ||
import contentType from "content-type"; | ||
import accepts from "accepts"; | ||
import cookie from "cookie"; | ||
import { MIMEType } from "node:util"; | ||
var WebRequest = class extends IncomingMessage { | ||
@@ -60,3 +66,6 @@ app; | ||
try { | ||
return contentType.parse(this).type; | ||
const contentType = this.headers["content-type"]; | ||
if (!contentType) return ""; | ||
const mime = new MIMEType(contentType); | ||
return mime.essence; | ||
} catch { | ||
@@ -173,11 +182,8 @@ return ""; | ||
import mimeTypes from "mime-types"; | ||
import { LRUCache } from "lru-cache"; | ||
var cache = new LRUCache({ | ||
max: 100 | ||
}); | ||
var cache = {}; | ||
var getMimeType = (filenameOrExt) => { | ||
let mimeType = cache.get(filenameOrExt); | ||
if (!mimeType) { | ||
let mimeType = cache[filenameOrExt]; | ||
if (mimeType === void 0) { | ||
mimeType = mimeTypes.contentType(filenameOrExt) || ""; | ||
cache.set(filenameOrExt, mimeType); | ||
cache[filenameOrExt] = mimeType; | ||
} | ||
@@ -188,8 +194,5 @@ return mimeType; | ||
// src/http/response.ts | ||
import contentType2 from "content-type"; | ||
import stream from "stream"; | ||
import destroy from "destroy"; | ||
import createHttpError, { isHttpError } from "http-errors"; | ||
import encodeUrl from "encodeurl"; | ||
import escapeHtml from "escape-html"; | ||
import contentDisposition from "content-disposition"; | ||
@@ -201,2 +204,4 @@ import { extname } from "node:path"; | ||
import cookie2 from "cookie"; | ||
import { MIMEType as MIMEType2 } from "node:util"; | ||
import { i18n as i18n3 } from "@aomex/core"; | ||
var WebResponse = class extends ServerResponse { | ||
@@ -207,2 +212,4 @@ app; | ||
_body = null; | ||
_bodyType = "null"; | ||
_jsonString = ""; | ||
/** | ||
@@ -217,3 +224,3 @@ * 是否明确设置过内容 | ||
_statusCode = 404; | ||
_updatingBodyType = false; | ||
_updatingContentType = false; | ||
constructor(req) { | ||
@@ -242,14 +249,17 @@ super(req); | ||
get contentType() { | ||
return this.hasHeader("Content-Type") ? contentType2.parse(this).type : ""; | ||
try { | ||
const contentType = this.getHeader("Content-Type"); | ||
if (!contentType) return ""; | ||
const mime = new MIMEType2(contentType); | ||
return mime.essence; | ||
} catch { | ||
return ""; | ||
} | ||
} | ||
set contentType(typeOrFilenameOrExt) { | ||
const mimeType = getMimeType(typeOrFilenameOrExt); | ||
try { | ||
if (!mimeType) throw new Error(""); | ||
contentType2.parse(mimeType); | ||
} catch { | ||
throw new TypeError(`\u4E0D\u5408\u6CD5\u7684\u7C7B\u578B\uFF1A'${typeOrFilenameOrExt}'`); | ||
} | ||
const msg = `\u4E0D\u5408\u6CD5\u7684\u7C7B\u578B\uFF1A'${typeOrFilenameOrExt}'`; | ||
if (!mimeType) throw new TypeError(msg); | ||
this.setHeader("Content-Type", mimeType); | ||
this.updateBodyType(); | ||
this.updateContentType(); | ||
} | ||
@@ -260,8 +270,12 @@ get body() { | ||
set body(val) { | ||
this._body = val == null ? null : val; | ||
val = this._body = val == null ? null : val; | ||
this._explicitBody = true; | ||
this._bodyType = this.getBodyType(val); | ||
if (this._bodyType === "json") { | ||
this._jsonString = JSON.stringify(val); | ||
} | ||
if (!this._explicitStatus) { | ||
this.statusCode = 200; | ||
} | ||
this.updateBodyType(); | ||
this.updateContentType(); | ||
} | ||
@@ -275,5 +289,3 @@ download(filePath, options = {}) { | ||
if (!this.writable || this.headersSent) return; | ||
let output = this.body; | ||
if (statuses.empty[this.statusCode]) return void this.end(); | ||
if (output === null) { | ||
if (this.body === null) { | ||
if (this.contentType === "application/json") { | ||
@@ -287,8 +299,11 @@ this.body = String(null); | ||
} | ||
output = this.body; | ||
} | ||
const { _bodyType } = this; | ||
if (statuses.empty[this.statusCode]) return void this.end(); | ||
if (this.req.method === "HEAD") return void this.end(); | ||
if (typeof output === "string") return void this.end(output); | ||
if (Buffer.isBuffer(output)) return void this.end(output); | ||
if (output instanceof Stream) { | ||
if (_bodyType === "json") return void this.end(this._jsonString); | ||
if (_bodyType === "string") return void this.end(this.body); | ||
if (_bodyType === "buffer") return void this.end(this.body); | ||
if (_bodyType === "stream") { | ||
const output = this.body; | ||
if (!output.listenerCount("error")) { | ||
@@ -302,10 +317,5 @@ output.once("error", this.onError); | ||
} | ||
return void this.end(JSON.stringify(output)); | ||
} | ||
isJSON(body2) { | ||
if (!body2) return false; | ||
if (typeof body2 === "string") return false; | ||
if (body2 instanceof Stream) return false; | ||
if (Buffer.isBuffer(body2)) return false; | ||
return true; | ||
isJSON(value) { | ||
return value === this.body ? this._bodyType === "json" : this.getBodyType(value) === "json"; | ||
} | ||
@@ -336,10 +346,12 @@ matchContentType(type, ...types) { | ||
this.statusCode = typeof status === "string" ? 302 : status; | ||
this.setHeader("location", encodeUrl(url)); | ||
if (this.req.accept.types("html")) { | ||
url = escapeHtml(url); | ||
this.contentType = "html"; | ||
this.body = `Redirecting to <a href="${url}">${url}</a>.`; | ||
const href = new URL(url).href; | ||
this.setHeader("location", href); | ||
if (this.req.accept.types("text/html")) { | ||
this.contentType = "text/html"; | ||
this.body = i18n3.t("web.response.redirect", { | ||
url: `<a href="${href}">${url.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'")}</a>` | ||
}); | ||
} else { | ||
this.contentType = "text"; | ||
this.body = `Redirecting to ${url}.`; | ||
this.contentType = "text/plain"; | ||
this.body = i18n3.t("web.response.redirect", { url }); | ||
} | ||
@@ -387,11 +399,22 @@ } | ||
} else { | ||
this.updateBodyType(); | ||
this.updateContentType(); | ||
} | ||
} | ||
updateBodyType() { | ||
if (this._updatingBodyType) return; | ||
this._updatingBodyType = true; | ||
const { body: body2 } = this; | ||
updateContentType() { | ||
if (this._updatingContentType) return; | ||
this._updatingContentType = true; | ||
const { _bodyType } = this; | ||
const missType = !this.hasHeader("Content-Type"); | ||
if (body2 === null) { | ||
if (_bodyType === "json") { | ||
this.contentLength = Buffer.byteLength(this._jsonString); | ||
if (missType) { | ||
this.contentType = "application/json"; | ||
} | ||
} else if (_bodyType === "string") { | ||
const body2 = this.body; | ||
this.contentLength = Buffer.byteLength(body2); | ||
if (missType) { | ||
this.contentType = /^\s*</.test(body2) ? "text/html" : "text/plain"; | ||
} | ||
} else if (_bodyType === "null") { | ||
if (statuses.empty[this.statusCode]) { | ||
@@ -403,24 +426,22 @@ this.removeHeaders("Content-Type", "Content-Length", "Transfer-Encoding"); | ||
} | ||
} else if (typeof body2 === "string") { | ||
this.contentLength = Buffer.byteLength(body2); | ||
if (missType) { | ||
this.contentType = /^\s*</.test(body2) ? "html" : "text"; | ||
} | ||
} else if (Buffer.isBuffer(body2)) { | ||
} else if (_bodyType === "buffer") { | ||
const body2 = this.body; | ||
this.contentLength = body2.length; | ||
if (missType) { | ||
this.contentType = "bin"; | ||
this.contentType = "application/octet-stream"; | ||
} | ||
} else if (body2 instanceof Stream) { | ||
} else if (_bodyType === "stream") { | ||
if (missType) { | ||
this.contentType = "bin"; | ||
this.contentType = "application/octet-stream"; | ||
} | ||
} else { | ||
this.contentLength = Buffer.byteLength(JSON.stringify(body2)); | ||
if (missType) { | ||
this.contentType = "json"; | ||
} | ||
} | ||
this._updatingBodyType = false; | ||
this._updatingContentType = false; | ||
} | ||
getBodyType(body2) { | ||
if (body2 == null) return "null"; | ||
if (typeof body2 === "string") return "string"; | ||
if (Buffer.isBuffer(body2)) return "buffer"; | ||
if (body2 instanceof Stream) return "stream"; | ||
return "json"; | ||
} | ||
}; | ||
@@ -432,3 +453,3 @@ | ||
flattenMiddlewareToken, | ||
i18n as i18n3 | ||
i18n as i18n4 | ||
} from "@aomex/core"; | ||
@@ -476,3 +497,3 @@ | ||
if (options.locale) { | ||
i18n3.setLocale(options.locale); | ||
i18n4.setLocale(options.locale); | ||
} | ||
@@ -576,3 +597,3 @@ if (options.mount) { | ||
Rule, | ||
i18n as i18n4 | ||
i18n as i18n5 | ||
} from "@aomex/core"; | ||
@@ -614,6 +635,6 @@ import { bytes } from "@aomex/internal-tools"; | ||
if (!(file instanceof PersistentFile)) { | ||
return magistrate.fail(i18n4.t("web.validator.file.must_be_file", { label })); | ||
return magistrate.fail(i18n5.t("web.validator.file.must_be_file", { label })); | ||
} | ||
if (maxSize !== void 0 && file.size > maxSize) { | ||
return magistrate.fail(i18n4.t("web.validator.file.too_large", { label })); | ||
return magistrate.fail(i18n5.t("web.validator.file.too_large", { label })); | ||
} | ||
@@ -623,3 +644,3 @@ const hasMimeTypeLimitation = mimeTypes3 && mimeTypes3.length; | ||
return magistrate.fail( | ||
i18n4.t("web.validator.file.unsupported_mimetype", { label }) | ||
i18n5.t("web.validator.file.unsupported_mimetype", { label }) | ||
); | ||
@@ -759,4 +780,4 @@ } | ||
getContentType() { | ||
let { contentType: contentType3, content } = this.options; | ||
if (!contentType3) { | ||
let { contentType, content } = this.options; | ||
if (!contentType) { | ||
const validator = toValidator(content); | ||
@@ -767,3 +788,3 @@ const docs = validator["toDocument"](); | ||
case "object": | ||
contentType3 = getMimeType("json"); | ||
contentType = getMimeType("json"); | ||
break; | ||
@@ -773,18 +794,18 @@ case "boolean": | ||
case "number": | ||
contentType3 = getMimeType("text"); | ||
contentType = getMimeType("text"); | ||
break; | ||
case "string": | ||
if (docs.format === "binary") { | ||
contentType3 = getMimeType("bin"); | ||
contentType = getMimeType("bin"); | ||
} else { | ||
contentType3 = getMimeType("text"); | ||
contentType = getMimeType("text"); | ||
} | ||
break; | ||
default: | ||
contentType3 = "*/*"; | ||
contentType = "*/*"; | ||
} | ||
} else if (!contentType3.includes("*")) { | ||
contentType3 = getMimeType(contentType3); | ||
} else if (!contentType.includes("*")) { | ||
contentType = getMimeType(contentType); | ||
} | ||
return contentType3.split(";")[0]; | ||
return contentType.split(";")[0]; | ||
} | ||
@@ -791,0 +812,0 @@ }; |
{ | ||
"name": "@aomex/web", | ||
"version": "1.0.4", | ||
"version": "1.1.0", | ||
"description": "aomex web层应用", | ||
@@ -42,11 +42,7 @@ "type": "module", | ||
"content-disposition": "^0.5.4", | ||
"content-type": "^1.0.5", | ||
"cookie": "^0.6.0", | ||
"destroy": "^1.2.0", | ||
"encodeurl": "^2.0.0", | ||
"escape-html": "^1.0.3", | ||
"formidable": "^3.5.1", | ||
"fresh": "^0.5.2", | ||
"http-errors": "^2.0.0", | ||
"lru-cache": "^10.2.0", | ||
"mime-types": "^2.1.35", | ||
@@ -58,10 +54,7 @@ "qs": "^6.12.0", | ||
"vary": "^1.1.2", | ||
"@aomex/internal-tools": "^1.0.4" | ||
"@aomex/internal-tools": "^1.1.0" | ||
}, | ||
"devDependencies": { | ||
"@types/co-body": "^6.1.3", | ||
"@types/content-type": "^1.1.8", | ||
"@types/destroy": "^1.0.3", | ||
"@types/encodeurl": "^1.0.2", | ||
"@types/escape-html": "^1.0.4", | ||
"@types/fresh": "^0.5.2", | ||
@@ -72,5 +65,5 @@ "@types/mime-types": "^2.1.4", | ||
"@types/vary": "^1.1.3", | ||
"@aomex/core": "^1.0.4" | ||
"@aomex/core": "^1.1.0" | ||
}, | ||
"scripts": {} | ||
} |
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
101718
23
8
1301
- Removedcontent-type@^1.0.5
- Removedencodeurl@^2.0.0
- Removedescape-html@^1.0.3
- Removedlru-cache@^10.2.0
- Removedcontent-type@1.0.5(transitive)
- Removedencodeurl@2.0.0(transitive)
- Removedescape-html@1.0.3(transitive)
- Removedlru-cache@10.4.3(transitive)
Updated@aomex/internal-tools@^1.1.0