phecda-server
Version:
server framework that provide IOC/type-reuse/http&rpc-adaptor
618 lines (542 loc) • 20.8 kB
JavaScript
;Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;
var _chunkLLF55NZPjs = require('./chunk-LLF55NZP.js');
// src/helper.ts
var _picocolors = require('picocolors'); var _picocolors2 = _interopRequireDefault(_picocolors);
// src/context.ts
var _debug = require('debug'); var _debug2 = _interopRequireDefault(_debug);
// src/pipe.ts
var _phecdacore = require('phecda-core');
// src/exception/base.ts
var Exception = class extends Error {
static {
_chunkLLF55NZPjs.__name.call(void 0, this, "Exception");
}
constructor(message, metadata, status = 0, description = "Exception") {
super(message), this.message = message, this.metadata = metadata, this.status = status, this.description = description;
}
get data() {
return {
message: this.message,
description: this.description,
status: this.status,
[_chunkLLF55NZPjs.ERROR_SYMBOL]: true
};
}
};
// src/exception/undefine.ts
var UndefinedException = class extends Exception {
static {
_chunkLLF55NZPjs.__name.call(void 0, this, "UndefinedException");
}
constructor(message, metadata) {
super(message, metadata, 500, "Undefined error");
}
};
// src/exception/validate.ts
var ValidateException = class extends Exception {
static {
_chunkLLF55NZPjs.__name.call(void 0, this, "ValidateException");
}
constructor(message, metadata) {
super(message, metadata, 400, "Validate exception");
}
};
// src/exception/forbidden.ts
var ForbiddenException = class extends Exception {
static {
_chunkLLF55NZPjs.__name.call(void 0, this, "ForbiddenException");
}
constructor(message, metadata) {
super(message, metadata, 403, "Forbidden resource");
}
};
// src/exception/bad-request.ts
var BadRequestException = class extends Exception {
static {
_chunkLLF55NZPjs.__name.call(void 0, this, "BadRequestException");
}
constructor(message, metadata) {
super(message, metadata, 400, "Bad Request");
}
};
// src/exception/not-found.ts
var NotFoundException = class extends Exception {
static {
_chunkLLF55NZPjs.__name.call(void 0, this, "NotFoundException");
}
constructor(message, metadata) {
super(message, metadata, 404, "Not Found");
}
};
// src/exception/conflict.ts
var ConflictException = class extends Exception {
static {
_chunkLLF55NZPjs.__name.call(void 0, this, "ConflictException");
}
constructor(message, metadata) {
super(message, metadata, 409, "Conflict");
}
};
// src/exception/bad-gateway.ts
var BadGatewayException = class extends Exception {
static {
_chunkLLF55NZPjs.__name.call(void 0, this, "BadGatewayException");
}
constructor(message, metadata) {
super(message, metadata, 502, "Bad Gatrway");
}
};
// src/exception/invalid-input.ts
var InvalidInputException = class extends Exception {
static {
_chunkLLF55NZPjs.__name.call(void 0, this, "InvalidInputException");
}
constructor(message, metadata) {
super(message, metadata, 502, "Invalid Input");
}
};
// src/exception/media-type.ts
var UnsupportedMediaTypeException = class extends Exception {
static {
_chunkLLF55NZPjs.__name.call(void 0, this, "UnsupportedMediaTypeException");
}
constructor(message, metadata) {
super(message, metadata, 415, "Unsupported Media Type");
}
};
// src/exception/payload-large.ts
var PayloadLargeException = class extends Exception {
static {
_chunkLLF55NZPjs.__name.call(void 0, this, "PayloadLargeException");
}
constructor(message, metadata) {
super(message, metadata, 413, "Payload Too Large");
}
};
// src/exception/timeout.ts
var TimeoutException = class extends Exception {
static {
_chunkLLF55NZPjs.__name.call(void 0, this, "TimeoutException");
}
constructor(message, metadata) {
super(message, metadata, 408, "Request Timeout");
}
};
// src/exception/unauthorized.ts
var UnauthorizedException = class extends Exception {
static {
_chunkLLF55NZPjs.__name.call(void 0, this, "UnauthorizedException");
}
constructor(message, metadata) {
super(message, metadata, 401, "Unauthorized");
}
};
// src/exception/unavailable-service.ts
var ServiceUnavailableException = class extends Exception {
static {
_chunkLLF55NZPjs.__name.call(void 0, this, "ServiceUnavailableException");
}
constructor(message, metadata) {
super(message, metadata, 503, "Service Unavailable");
}
};
// src/exception/framework.ts
var FrameworkException = class extends Exception {
static {
_chunkLLF55NZPjs.__name.call(void 0, this, "FrameworkException");
}
constructor(message, metadata) {
super(`[phecda-server] ${message}`, metadata, 500, "Framework Error");
}
};
// src/exception/timer.ts
var TimerException = class extends Exception {
static {
_chunkLLF55NZPjs.__name.call(void 0, this, "TimerException");
}
constructor(message, metadata) {
super(message, metadata, 0, "Timer Error");
}
};
// src/exception/worker.ts
var WorkerException = class extends Exception {
static {
_chunkLLF55NZPjs.__name.call(void 0, this, "WorkerException");
}
constructor(message, metadata) {
super(message, metadata, 0, "Worker Error");
}
};
// src/pipe.ts
var defaultPipe = /* @__PURE__ */ _chunkLLF55NZPjs.__name.call(void 0, async ({ arg, reflect, meta, index, type }, { method }) => {
if (meta.const) {
if (arg !== meta.const) throw new ValidateException(`param ${index + 1} must be ${meta.const}`);
}
if (arg === void 0) {
if (meta.required === false) return arg;
else throw new ValidateException(`param ${index + 1} is required`);
}
if ([
"params",
"query"
].includes(type)) {
if (reflect === Number) {
arg = reflect(arg);
if (isNaN(arg)) throw new ValidateException(`param ${index + 1} is not a number`);
} else if (reflect === Boolean) {
if (arg === "false") arg = false;
else if (arg === "true") arg = true;
else throw new ValidateException(`param ${index + 1} is not a boolean`);
}
} else {
if (reflect === Number && typeof arg !== "number") throw new ValidateException(`param ${index + 1} is not a number`);
if (reflect === Boolean && typeof arg !== "boolean") throw new ValidateException(`param ${index + 1} is not a boolean`);
if (reflect === String && typeof arg !== "string") throw new ValidateException(`param ${index + 1} is not a string`);
}
if (meta.enum) {
if (!Object.values(meta.enum).includes(arg)) throw new ValidateException(`param ${index + 1} is not a valid enum value`);
}
if (meta.oneOf) {
let isCorrect = false;
for (const item of meta.oneOf) {
switch (item) {
case String:
if (typeof arg === "string") isCorrect = true;
break;
case Number:
if (typeof arg === "number") isCorrect = true;
break;
case Boolean:
if (typeof arg === "boolean") isCorrect = true;
break;
default:
if (_phecdacore.isPhecda.call(void 0, item)) {
const errs = await _phecdacore.validate.call(void 0, item, arg);
if (!errs.length) {
isCorrect = true;
break;
}
} else if (typeof item === "function") {
const ret = await item(arg);
if (ret) {
isCorrect = true;
break;
}
} else {
if (arg === item) {
isCorrect = true;
break;
}
}
}
}
if (!isCorrect) throw new ValidateException(`param ${index + 1} can't pass one of these validations`);
}
if (meta.rules) {
for (const rule of meta.rules) {
const err = await rule({
value: arg,
property: method,
meta,
model: reflect,
index
});
if (err.length > 0) throw new ValidateException(err[0]);
}
}
if (_phecdacore.isPhecda.call(void 0, reflect)) {
const errs = await _phecdacore.validate.call(void 0, reflect, arg);
if (errs.length) throw new ValidateException(errs[0]);
}
return arg;
}, "defaultPipe");
// src/filter.ts
var defaultFilter = /* @__PURE__ */ _chunkLLF55NZPjs.__name.call(void 0, (e) => {
if (!(e instanceof Exception)) {
_chunkLLF55NZPjs.log.call(void 0, e.message, "error");
if (_chunkLLF55NZPjs.LOG_LEVEL <= 0) console.error(e.stack);
e = new UndefinedException(e.message || e);
} else {
_chunkLLF55NZPjs.log.call(void 0, `[${e.constructor.name}] ${e.message}`, "error");
if (_chunkLLF55NZPjs.LOG_LEVEL <= 0) console.error(e.stack);
}
return e.data;
}, "defaultFilter");
// src/context.ts
var debug = _debug2.default.call(void 0, "phecda-server(Context)");
var Context = (_class = class _Context {
static {
_chunkLLF55NZPjs.__name.call(void 0, this, "Context");
}
static __initStatic() {this.filterRecord = {
default: defaultFilter
}}
static __initStatic2() {this.pipeRecord = {
default: defaultPipe
}}
static __initStatic3() {this.guardRecord = {}}
static __initStatic4() {this.addonRecord = {}}
// protected canGetCtx = true
constructor(data) {
this.data = data;
if (_chunkLLF55NZPjs.IS_DEV)
data._context = this;
this.ctx = new Proxy(data, {
get(target, p) {
if (!(p in target)) _chunkLLF55NZPjs.log.call(void 0, `attribute "${p}" does not exist on ctx, which might be due to a missing AOP role (such as a guard).`, "warn", data.tag);
return target[p];
},
set(target, p, newValue) {
target[p] = newValue;
return true;
}
});
}
static getAop(meta, opts) {
const { globalGuards = [], globalFilter = "default", globalPipe = "default" } = opts;
const { data: { guards, filter, params, tag, method } } = meta;
const resolved = {
guards: [
...globalGuards,
...guards
],
pipe: params.map((item) => item.pipe || globalPipe),
filter: filter || globalFilter
};
if (process.env.DEBUG) {
const { guards: guards2, pipe, filter: filter2 } = resolved;
debug(`method "${tag}-${method}" aop:
${_picocolors2.default.magenta(`Guard ${guards2.join("->")}[${guards2.filter((g) => g in this.guardRecord).join("->")}]`)}
${_picocolors2.default.blue(`Pipe ${pipe.join("-")}[${pipe.map((p) => p in this.pipeRecord ? p : "default").join("-")}]`)}
${_picocolors2.default.red(`Filter ${filter2}[${filter2 || "default"}]`)}`);
}
return {
guards: this.getGuards(resolved.guards),
pipe: this.getPipe(resolved.pipe),
filter: this.getFilter(resolved.filter)
};
}
async run({ guards, filter, pipe }, successCb, failCb) {
const { meta, moduleMap } = this.data;
const { paramsType, data: { ctxs, tag, params, method } } = meta;
try {
let res;
const nextHandler = /* @__PURE__ */ _chunkLLF55NZPjs.__name.call(void 0, (index) => {
return async () => {
if (index === guards.length) {
const instance = moduleMap.get(tag);
if (ctxs) {
ctxs.forEach((ctx) => instance[ctx] = this.ctx);
}
const args = await Promise.all(params.map((item, i) => pipe[i]({
arg: resolveDep(this.data[item.type], item.key),
reflect: paramsType[item.index],
...item
}, this.ctx)));
res = await instance[method](...args);
} else {
let nextPromise;
async function next() {
return nextPromise = nextHandler(index + 1)().then((ret2) => {
if (ret2 !== void 0) {
debug(`The ${index + 1}th guard on "${tag}-${method}" rewrite the response value.`);
res = ret2;
}
return res;
});
}
_chunkLLF55NZPjs.__name.call(void 0, next, "next");
const ret = await guards[index](this.ctx, next);
if (ret !== void 0) {
res = ret;
} else {
if (!nextPromise) await next();
else await nextPromise;
}
}
};
}, "nextHandler");
await nextHandler(0)();
return successCb(res);
} catch (e) {
const err = await filter(e, this.ctx);
return failCb(err);
}
}
static getPipe(pipe) {
return pipe.map((pipe2) => {
return _Context.pipeRecord[pipe2] || _Context.pipeRecord.default;
});
}
static getFilter(filter = "default") {
return _Context.filterRecord[filter] || _Context.filterRecord.default;
}
static getGuards(guards) {
const ret = [];
for (const guard of new Set(guards)) {
if (guard in _Context.guardRecord) ret.push(_Context.guardRecord[guard]);
}
return ret.sort((a, b) => b.priority - a.priority).map((item) => item.value);
}
static applyAddons(addons, router, framework) {
const ret = [];
for (const a of new Set(addons)) {
if (a in _Context.addonRecord) ret.push(_Context.addonRecord[a]);
}
ret.sort((a, b) => b.priority - a.priority).forEach((item) => item.value(router, framework));
}
}, _class.__initStatic(), _class.__initStatic2(), _class.__initStatic3(), _class.__initStatic4(), _class);
function addPipe(key, pipe) {
if (Context.pipeRecord[key] && Context.pipeRecord[key] !== pipe) debug(`overwrite Pipe "${String(key)}"`, "warn");
Context.pipeRecord[key] = pipe;
}
_chunkLLF55NZPjs.__name.call(void 0, addPipe, "addPipe");
function addFilter(key, filter) {
if (Context.filterRecord[key] && Context.filterRecord[key] !== filter) debug(`overwrite Filter "${String(key)}"`, "warn");
Context.filterRecord[key] = filter;
}
_chunkLLF55NZPjs.__name.call(void 0, addFilter, "addFilter");
function addGuard(key, guard, priority = 0) {
if (Context.guardRecord[key] && Context.guardRecord[key].value !== guard) debug(`overwrite Guard "${String(key)}"`, "warn");
Context.guardRecord[key] = {
value: guard,
priority
};
}
_chunkLLF55NZPjs.__name.call(void 0, addGuard, "addGuard");
function addAddon(key, addon, priority = 0) {
if (Context.addonRecord[key] && Context.addonRecord[key].value !== addon) debug(`overwrite Addon "${String(key)}"`, "warn");
Context.addonRecord[key] = {
value: addon,
priority
};
}
_chunkLLF55NZPjs.__name.call(void 0, addAddon, "addAddon");
// src/http/helper.ts
function resolveDep(ret, key) {
if (key) return _optionalChain([ret, 'optionalAccess', _ => _[key]]);
return ret;
}
_chunkLLF55NZPjs.__name.call(void 0, resolveDep, "resolveDep");
// src/decorators/helper.ts
function shallowClone(obj) {
return {
...obj
};
}
_chunkLLF55NZPjs.__name.call(void 0, shallowClone, "shallowClone");
function mergeObject(...args) {
return Object.assign({}, ...args);
}
_chunkLLF55NZPjs.__name.call(void 0, mergeObject, "mergeObject");
// src/helper.ts
function createControllerMetaMap(meta, filter) {
const metaMap = /* @__PURE__ */ new Map();
function handleMeta() {
metaMap.clear();
for (const item of meta) {
const { tag, method } = item.data;
if (!filter(item)) continue;
if (metaMap.has(tag)) metaMap.get(tag)[method] = item;
else metaMap.set(tag, {
[method]: item
});
}
}
_chunkLLF55NZPjs.__name.call(void 0, handleMeta, "handleMeta");
handleMeta();
_chunkLLF55NZPjs.HMR.call(void 0, handleMeta);
return metaMap;
}
_chunkLLF55NZPjs.__name.call(void 0, createControllerMetaMap, "createControllerMetaMap");
function detectAopDep(meta, { guards, addons } = {}, controller = "http") {
if (_chunkLLF55NZPjs.IS_PURE) return;
const addonSet = /* @__PURE__ */ new Set();
const guardSet = /* @__PURE__ */ new Set();
const pipeSet = /* @__PURE__ */ new Set();
const filterSet = /* @__PURE__ */ new Set();
const warningSet = /* @__PURE__ */ new Set();
function handleMeta() {
addonSet.clear();
guardSet.clear();
pipeSet.clear();
filterSet.clear();
warningSet.clear();
_optionalChain([addons, 'optionalAccess', _2 => _2.forEach, 'call', _3 => _3((item) => {
addonSet.add(item);
})]);
_optionalChain([guards, 'optionalAccess', _4 => _4.forEach, 'call', _5 => _5((item) => {
guardSet.add(item);
})]);
meta.forEach(({ data }) => {
if (!data.controller) return;
if (typeof data.tag !== "string") warningSet.add(`Tag of controller "${data.name}" should be a string`);
if (data.controller !== controller) {
if (data[controller]) warningSet.add(`Should use ${controller} controller to decorate class "${data.name}"`);
return;
}
if (data.filter) filterSet.add(data.filter);
data.guards.forEach((i) => guardSet.add(i));
data.addons.forEach((i) => addonSet.add(i));
data.params.forEach((i) => {
if (i.pipe) pipeSet.add(i.pipe);
});
});
const missAddons = [
...addonSet
].filter((i) => !Context.addonRecord[i]);
const missGuards = [
...guardSet
].filter((i) => !Context.guardRecord[i]);
const missPipes = [
...pipeSet
].filter((i) => !Context.pipeRecord[i]);
const missFilters = [
...filterSet
].filter((i) => !Context.filterRecord[i]);
function exit() {
if (_chunkLLF55NZPjs.IS_STRICT) {
_chunkLLF55NZPjs.log.call(void 0, "Does not meet strict mode requirements", "error");
process.exit(1);
}
}
_chunkLLF55NZPjs.__name.call(void 0, exit, "exit");
if (missAddons.length) {
_chunkLLF55NZPjs.log.call(void 0, `${_picocolors2.default.white(`Addon [${missAddons.join(",")}]`)} doesn't exist`, "warn");
exit();
}
if (missGuards.length) {
_chunkLLF55NZPjs.log.call(void 0, `${_picocolors2.default.magenta(`Guard [${missGuards.join(",")}]`)} doesn't exist`, "warn");
exit();
}
if (missPipes.length) {
_chunkLLF55NZPjs.log.call(void 0, `${_picocolors2.default.blue(`Pipe [${missPipes.join(",")}]`)} doesn't exist`, "warn");
exit();
}
if (missFilters.length) {
_chunkLLF55NZPjs.log.call(void 0, `${_picocolors2.default.red(`Filter [${missFilters.join(",")}]`)} doesn't exist`, "warn");
exit();
}
warningSet.forEach((warn) => _chunkLLF55NZPjs.log.call(void 0, warn, "warn"));
if (warningSet.size) exit();
}
_chunkLLF55NZPjs.__name.call(void 0, handleMeta, "handleMeta");
handleMeta();
_chunkLLF55NZPjs.HMR.call(void 0, handleMeta);
return {
addonSet,
guardSet,
pipeSet,
filterSet
};
}
_chunkLLF55NZPjs.__name.call(void 0, detectAopDep, "detectAopDep");
function joinUrl(base, ...paths) {
const joinedPath = [
base,
...paths
].filter((p) => p).map((path) => path.replace(/(^\/)/g, "")).join("/");
return `/${joinedPath}`;
}
_chunkLLF55NZPjs.__name.call(void 0, joinUrl, "joinUrl");
exports.Exception = Exception; exports.UndefinedException = UndefinedException; exports.ValidateException = ValidateException; exports.ForbiddenException = ForbiddenException; exports.BadRequestException = BadRequestException; exports.NotFoundException = NotFoundException; exports.ConflictException = ConflictException; exports.BadGatewayException = BadGatewayException; exports.InvalidInputException = InvalidInputException; exports.UnsupportedMediaTypeException = UnsupportedMediaTypeException; exports.PayloadLargeException = PayloadLargeException; exports.TimeoutException = TimeoutException; exports.UnauthorizedException = UnauthorizedException; exports.ServiceUnavailableException = ServiceUnavailableException; exports.FrameworkException = FrameworkException; exports.TimerException = TimerException; exports.WorkerException = WorkerException; exports.defaultPipe = defaultPipe; exports.defaultFilter = defaultFilter; exports.Context = Context; exports.addPipe = addPipe; exports.addFilter = addFilter; exports.addGuard = addGuard; exports.addAddon = addAddon; exports.resolveDep = resolveDep; exports.shallowClone = shallowClone; exports.mergeObject = mergeObject; exports.createControllerMetaMap = createControllerMetaMap; exports.detectAopDep = detectAopDep; exports.joinUrl = joinUrl;