@aomex/web
Advanced tools
Comparing version 1.7.0 to 2.0.0
@@ -6,2 +6,18 @@ # Change Log | ||
# [2.0.0](https://github.com/aomex/aomex/compare/v1.7.0...v2.0.0) (2024-07-26) | ||
### Features | ||
* **web:** router库合并到web库 ([7268973](https://github.com/aomex/aomex/commit/72689738b9a7eb1eb77c9b0bb651d47e58ed5532)) | ||
### BREAKING CHANGES | ||
* **web:** @aomex/router已被删除 | ||
# [1.7.0](https://github.com/aomex/aomex/compare/v1.6.0...v1.7.0) (2024-07-13) | ||
@@ -8,0 +24,0 @@ |
@@ -1,2 +0,2 @@ | ||
import { I18nMessage, Next, Middleware, MixinMiddleware, OpenAPI, Validator, TransformedValidator, magistrate, I18n, ValidatorToken } from '@aomex/core'; | ||
import { I18nMessage, Next, Middleware, MixinMiddleware, OpenAPI, Validator, TransformedValidator, magistrate, I18n, ValidatorToken, ComposeFn } from '@aomex/core'; | ||
import * as net from 'net'; | ||
@@ -12,2 +12,4 @@ import http, { IncomingMessage, ServerResponse, OutgoingHttpHeaders } from 'node:http'; | ||
import { File } from 'formidable'; | ||
import { GlobPathOptions } from '@aomex/internal-file-import'; | ||
import { match } from 'path-to-regexp'; | ||
export { default as statuses } from 'statuses'; | ||
@@ -493,2 +495,66 @@ | ||
export { type Body, type BodyType, FileValidator, type HttpErrorProperties, type OpenApiInjector, WebApp, WebContext, WebMiddleware, type WebMiddlewareToken, WebRequest, WebResponse, WebResponseMiddleware, type WebResponseOptions, body, params, query, response }; | ||
interface BuilderOptions<Props extends object | unknown, T extends WebMiddlewareToken[] | []> { | ||
/** | ||
* 禁用当前路由。默认 `false` | ||
*/ | ||
disable?: boolean; | ||
mount?: T; | ||
docs?: Builder.Docs; | ||
action: (ctx: Builder.Context<Props, T>) => any; | ||
} | ||
declare namespace Builder { | ||
type DTO<Props extends object | unknown, T extends WebMiddlewareToken[] | []> = Omit<Props & Union2Intersection<Middleware.CollectArrayType<T[number]>>, ResponseMethods>; | ||
type Interface<Props extends object | unknown, T extends WebMiddlewareToken[] | []> = Props & Union2Intersection<Middleware.CollectArrayType<T[number]>>; | ||
type Context<Props extends object | unknown, T extends WebMiddlewareToken[] | []> = Interface<Props, T> & OverrideWebContext<Interface<Props, T>>; | ||
type ResponseMethods = 'send' | 'throw' | 'redirect'; | ||
type OverrideWebContext<Props extends object | unknown> = 'send' extends keyof Props ? Omit<WebContext, ResponseMethods> : 'throw' extends keyof Props ? Omit<WebContext, ResponseMethods> : 'redirect' extends keyof Props ? Omit<WebContext, ResponseMethods> : WebContext; | ||
type Docs = Omit<OpenAPI.OperationObject, 'parameters' | 'requestBody' | 'responses'> & { | ||
/** | ||
* 生成openapi时是否展示当前路由。默认:`true` | ||
*/ | ||
showInOpenapi?: boolean; | ||
}; | ||
} | ||
declare class Builder<Props extends object | unknown = object, T extends WebMiddlewareToken[] | [] = []> { | ||
protected readonly methods: readonly (typeof Builder.METHODS)[number][]; | ||
static METHODS: readonly ["GET", "POST", "PUT", "PATCH", "DELETE"]; | ||
readonly uri: string; | ||
readonly docs: Builder.Docs; | ||
protected readonly middlewareList: WebMiddlewareToken[]; | ||
protected readonly matchFn: ReturnType<typeof match>; | ||
constructor(prefix: string, uri: string, methods: readonly (typeof Builder.METHODS)[number][], options: BuilderOptions<Props, T>); | ||
isPureUri(): boolean; | ||
match(pathname: string): Record<string, unknown> | false; | ||
} | ||
interface RouterOptions<T extends WebMiddlewareToken[] | [] = any[]> { | ||
prefix?: string; | ||
mount?: T; | ||
docs?: Pick<OpenAPI.OperationObject, 'tags' | 'deprecated'>; | ||
} | ||
declare class Router<T extends WebMiddlewareToken[] | [] = any[], Props extends object | unknown = WebApp.Props & Union2Intersection<Middleware.CollectArrayType<T[number]>>> { | ||
protected readonly opts: RouterOptions<T>; | ||
protected readonly middlewareList: WebMiddlewareToken[]; | ||
protected readonly prefix: string; | ||
protected readonly builders: Builder[]; | ||
protected readonly docs: RouterOptions['docs']; | ||
constructor(opts?: RouterOptions<T>); | ||
get<T extends WebMiddlewareToken[] | []>(uri: string, options: BuilderOptions<Props, T>): Builder.DTO<Props, T>; | ||
post<T extends WebMiddlewareToken[] | []>(uri: string, options: BuilderOptions<Props, T>): Builder.DTO<Props, T>; | ||
put<T extends WebMiddlewareToken[] | []>(uri: string, options: BuilderOptions<Props, T>): Builder.DTO<Props, T>; | ||
patch<T extends WebMiddlewareToken[] | []>(uri: string, options: BuilderOptions<Props, T>): Builder.DTO<Props, T>; | ||
delete<T extends WebMiddlewareToken[] | []>(uri: string, options: BuilderOptions<Props, T>): Builder.DTO<Props, T>; | ||
all<T extends WebMiddlewareToken[] | []>(uri: string, options: BuilderOptions<Props, T>): Builder.DTO<Props, T>; | ||
customize<T extends WebMiddlewareToken[] | []>(methods: (typeof Builder.METHODS)[number][], uri: string, options: BuilderOptions<Props, T>): Builder.DTO<Props, T>; | ||
protected create(uri: string, methods: readonly (typeof Builder.METHODS)[number][], options: BuilderOptions<Props, any[]>): any; | ||
protected collect(): Record<"GET" | "POST" | "PUT" | "PATCH" | "DELETE", { | ||
uri: string; | ||
isPureUri: boolean; | ||
match: Builder["match"]; | ||
route: ComposeFn; | ||
}[]>; | ||
} | ||
declare const routers: (options: GlobPathOptions | Router[]) => WebMiddleware; | ||
export { type Body, type BodyType, Builder, type BuilderOptions, FileValidator, type HttpErrorProperties, type OpenApiInjector, Router, type RouterOptions, WebApp, WebContext, WebMiddleware, type WebMiddlewareToken, WebRequest, WebResponse, WebResponseMiddleware, type WebResponseOptions, body, params, query, response, routers }; |
@@ -812,2 +812,185 @@ // src/i18n/locales/zh-cn.ts | ||
// src/router/routers.ts | ||
import { middleware as middleware6 } from "@aomex/core"; | ||
import { | ||
pathToFiles, | ||
getFileValues | ||
} from "@aomex/internal-file-import"; | ||
// src/router/router.ts | ||
import { compose as compose2 } from "@aomex/core"; | ||
// src/router/builder.ts | ||
import { middleware as middleware5 } from "@aomex/core"; | ||
import { match } from "path-to-regexp"; | ||
var pureUriPattern = /^[\/a-z0-9-_]+$/i; | ||
var duplicatedSlash = /\/{2,}/g; | ||
var sideSlash = /(^\/|\/$)/g; | ||
var Builder = class { | ||
constructor(prefix, uri, methods, options) { | ||
this.methods = methods; | ||
this.uri = "/" + (prefix + uri).replaceAll(duplicatedSlash, "/").replaceAll(sideSlash, ""); | ||
this.docs = options.docs || {}; | ||
this.docs.showInOpenapi ??= true; | ||
this.middlewareList = [ | ||
...options.mount || [], | ||
middleware5.web((ctx, _) => options.action(ctx)) | ||
]; | ||
this.matchFn = match(this.uri, { decode: decodeURIComponent }); | ||
} | ||
static METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"]; | ||
uri; | ||
docs; | ||
middlewareList; | ||
matchFn; | ||
isPureUri() { | ||
return pureUriPattern.test(this.uri); | ||
} | ||
match(pathname) { | ||
const matchResult = this.matchFn(pathname); | ||
return matchResult ? matchResult.params : false; | ||
} | ||
}; | ||
// src/router/router.ts | ||
var Router = class { | ||
constructor(opts = {}) { | ||
this.opts = opts; | ||
this.prefix = opts.prefix || ""; | ||
this.middlewareList = opts.mount || []; | ||
this.docs = opts.docs || {}; | ||
} | ||
middlewareList; | ||
prefix; | ||
builders = []; | ||
docs; | ||
get(uri, options) { | ||
return this.create(uri, ["GET"], options); | ||
} | ||
post(uri, options) { | ||
return this.create(uri, ["POST"], options); | ||
} | ||
put(uri, options) { | ||
return this.create(uri, ["PUT"], options); | ||
} | ||
patch(uri, options) { | ||
return this.create(uri, ["PATCH"], options); | ||
} | ||
delete(uri, options) { | ||
return this.create(uri, ["DELETE"], options); | ||
} | ||
all(uri, options) { | ||
return this.create(uri, Builder.METHODS, options); | ||
} | ||
customize(methods, uri, options) { | ||
return this.create(uri, methods, options); | ||
} | ||
create(uri, methods, options) { | ||
options.docs = { ...this.docs, ...options.docs }; | ||
const builder = new Builder(this.prefix, uri, methods, options); | ||
options.disable !== true && this.builders.push(builder); | ||
} | ||
collect() { | ||
const collections = { | ||
GET: [], | ||
POST: [], | ||
PUT: [], | ||
PATCH: [], | ||
DELETE: [] | ||
}; | ||
for (let i = 0; i < this.builders.length; ++i) { | ||
const builder = this.builders[i]; | ||
const handler = { | ||
uri: builder.uri, | ||
isPureUri: builder.isPureUri(), | ||
match: builder.match.bind(builder), | ||
route: compose2(this.middlewareList.concat(builder["middlewareList"])) | ||
}; | ||
for (const method of builder["methods"]) { | ||
collections[method].push(handler); | ||
} | ||
} | ||
return collections; | ||
} | ||
}; | ||
// src/router/routers.ts | ||
var routers = (options) => { | ||
let initialized = false; | ||
const staticCollections = { | ||
GET: {}, | ||
POST: {}, | ||
PUT: {}, | ||
PATCH: {}, | ||
DELETE: {}, | ||
OPTIONS: {}, | ||
HEAD: {} | ||
}; | ||
const dynamicCollections = { | ||
GET: [], | ||
POST: [], | ||
PUT: [], | ||
PATCH: [], | ||
DELETE: [], | ||
OPTIONS: [], | ||
HEAD: [] | ||
}; | ||
staticCollections.HEAD = staticCollections.GET; | ||
dynamicCollections.HEAD = dynamicCollections.GET; | ||
let routers2; | ||
let promise = Promise.resolve(); | ||
if (isRouters(options)) { | ||
routers2 = options; | ||
} else { | ||
promise = pathToFiles(options).then(async (files) => { | ||
routers2 = await getFileValues( | ||
files, | ||
(item) => !!item && item instanceof Router | ||
); | ||
}); | ||
} | ||
const initialize = () => { | ||
for (let i = 0; i < routers2.length; ++i) { | ||
const router = routers2[i]; | ||
const subCollections = router["collect"](); | ||
const methods = Object.keys( | ||
subCollections | ||
); | ||
methods.forEach((method) => { | ||
subCollections[method].forEach((item) => { | ||
if (item.isPureUri) { | ||
if (!Object.hasOwn(staticCollections[method], item.uri)) { | ||
staticCollections[method][item.uri] = item.route; | ||
} | ||
} else { | ||
dynamicCollections[method].push({ match: item.match, route: item.route }); | ||
} | ||
}); | ||
}); | ||
} | ||
initialized = true; | ||
}; | ||
return middleware6.web(async (ctx, next) => { | ||
if (!routers2) await promise; | ||
if (!initialized) initialize(); | ||
const { method, pathname } = ctx.request; | ||
const staticCollection = staticCollections[method] || {}; | ||
if (Object.hasOwn(staticCollection, pathname)) { | ||
return staticCollection[pathname](ctx, next); | ||
} | ||
const dynamicCollection = dynamicCollections[method] || []; | ||
for (let i = 0, len = dynamicCollection.length; i < len; ++i) { | ||
const params2 = dynamicCollection[i].match(pathname); | ||
if (params2) { | ||
ctx.request.params = params2; | ||
return dynamicCollection[i].route(ctx, next); | ||
} | ||
} | ||
return next(); | ||
}); | ||
}; | ||
var isRouters = (options) => { | ||
return Array.isArray(options) && options.some((item) => item instanceof Router); | ||
}; | ||
// src/index.ts | ||
@@ -817,3 +1000,5 @@ import { default as default2 } from "http-errors"; | ||
export { | ||
Builder, | ||
FileValidator, | ||
Router, | ||
WebApp, | ||
@@ -830,4 +1015,5 @@ WebContext, | ||
response, | ||
routers, | ||
default3 as statuses | ||
}; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@aomex/web", | ||
"version": "1.7.0", | ||
"version": "2.0.0", | ||
"description": "aomex web层应用", | ||
@@ -49,2 +49,3 @@ "type": "module", | ||
"mime-types": "^2.1.35", | ||
"path-to-regexp": "^7.0.0", | ||
"request-ip": "^3.3.0", | ||
@@ -54,3 +55,4 @@ "statuses": "^2.0.1", | ||
"vary": "^1.1.2", | ||
"@aomex/internal-tools": "^1.7.0" | ||
"@aomex/internal-tools": "^2.0.0", | ||
"@aomex/internal-file-import": "^2.0.0" | ||
}, | ||
@@ -66,5 +68,5 @@ "devDependencies": { | ||
"@types/vary": "^1.1.3", | ||
"@aomex/core": "^1.7.0" | ||
"@aomex/core": "^2.0.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
127040
1539
22
+ Addedpath-to-regexp@^7.0.0
+ Added@aomex/internal-file-import@2.2.0(transitive)
+ Added@aomex/internal-tools@2.2.0(transitive)
+ Added@isaacs/cliui@8.0.2(transitive)
+ Added@pkgjs/parseargs@0.11.0(transitive)
+ Addedansi-regex@5.0.16.1.0(transitive)
+ Addedansi-styles@4.3.06.2.1(transitive)
+ Addedbalanced-match@1.0.2(transitive)
+ Addedbrace-expansion@2.0.1(transitive)
+ Addedcolor-convert@2.0.1(transitive)
+ Addedcolor-name@1.1.4(transitive)
+ Addedcross-spawn@7.0.3(transitive)
+ Addedeastasianwidth@0.2.0(transitive)
+ Addedemoji-regex@8.0.09.2.2(transitive)
+ Addedforeground-child@3.3.0(transitive)
+ Addedglob@10.4.5(transitive)
+ Addedis-fullwidth-code-point@3.0.0(transitive)
+ Addedisexe@2.0.0(transitive)
+ Addedjackspeak@3.4.3(transitive)
+ Addedlru-cache@10.4.3(transitive)
+ Addedminimatch@9.0.5(transitive)
+ Addedminipass@7.1.2(transitive)
+ Addedpackage-json-from-dist@1.0.0(transitive)
+ Addedpath-key@3.1.1(transitive)
+ Addedpath-scurry@1.11.1(transitive)
+ Addedpath-to-regexp@7.2.0(transitive)
+ Addedshebang-command@2.0.0(transitive)
+ Addedshebang-regex@3.0.0(transitive)
+ Addedsignal-exit@4.1.0(transitive)
+ Addedstring-width@4.2.35.1.2(transitive)
+ Addedstrip-ansi@6.0.17.1.0(transitive)
+ Addedwhich@2.0.2(transitive)
+ Addedwrap-ansi@7.0.08.1.0(transitive)
Updated@aomex/internal-tools@^2.0.0