elysia
Version:
Ergonomic Framework for Human
1,294 lines • 70 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf, __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: !0 });
}, __copyProps = (to, from, except, desc) => {
if (from && typeof from == "object" || typeof from == "function")
for (let key of __getOwnPropNames(from))
!__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: !0 }) : target,
mod
)), __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: !0 }), mod);
var index_exports = {};
__export(index_exports, {
Cookie: () => import_cookies.Cookie,
ELYSIA_FORM_DATA: () => import_utils4.ELYSIA_FORM_DATA,
ELYSIA_REQUEST_ID: () => import_utils4.ELYSIA_REQUEST_ID,
ELYSIA_TRACE: () => import_trace2.ELYSIA_TRACE,
ERROR_CODE: () => import_error2.ERROR_CODE,
Elysia: () => Elysia,
ElysiaCustomStatusResponse: () => import_error2.ElysiaCustomStatusResponse,
ElysiaFile: () => import_file.ElysiaFile,
InternalServerError: () => import_error2.InternalServerError,
InvalidCookieSignature: () => import_error2.InvalidCookieSignature,
InvalidFileType: () => import_error2.InvalidFileType,
InvertedStatusMap: () => import_utils4.InvertedStatusMap,
NotFoundError: () => import_error2.NotFoundError,
ParseError: () => import_error2.ParseError,
StatusMap: () => import_utils4.StatusMap,
TypeSystemPolicy: () => import_system.TypeSystemPolicy,
ValidationError: () => import_error2.ValidationError,
checksum: () => import_utils4.checksum,
cloneInference: () => import_utils4.cloneInference,
deduplicateChecksum: () => import_utils4.deduplicateChecksum,
default: () => Elysia,
env: () => import_env2.env,
file: () => import_file.file,
fileType: () => import_utils3.fileType,
form: () => import_utils4.form,
getResponseSchemaValidator: () => import_schema2.getResponseSchemaValidator,
getSchemaValidator: () => import_schema2.getSchemaValidator,
mapValueError: () => import_error2.mapValueError,
mergeHook: () => import_utils4.mergeHook,
mergeObjectArray: () => import_utils4.mergeObjectArray,
redirect: () => import_utils4.redirect,
replaceSchemaType: () => import_replace_schema2.replaceSchemaTypeFromManyOptions,
replaceUrlPath: () => import_utils4.replaceUrlPath,
serializeCookie: () => import_cookies.serializeCookie,
sse: () => import_utils4.sse,
status: () => import_error2.status,
t: () => import_type_system2.t,
validationDetail: () => import_utils3.validationDetail
});
module.exports = __toCommonJS(index_exports);
var import_memoirist = require("memoirist"), import_typebox = require("@sinclair/typebox"), import_fast_decode_uri_component = __toESM(require("fast-decode-uri-component")), import_type_system = require('./type-system/index.js'), import_sucrose = require('./sucrose.js'), import_bun = require('./adapter/bun/index.js'), import_web_standard = require('./adapter/web-standard/index.js'), import_env = require('./universal/env.js'), import_utils = require('./utils.js'), import_schema = require('./schema.js'), import_compose = require('./compose.js'), import_trace = require('./trace.js'), import_utils2 = require('./utils.js'), import_dynamic_handle = require('./dynamic-handle.js'), import_error = require('./error.js'), import_replace_schema = require('./replace-schema.js'), import_type_system2 = require('./type-system/index.js'), import_utils3 = require('./type-system/utils.js'), import_cookies = require('./cookies.js'), import_trace2 = require('./trace.js'), import_schema2 = require('./schema.js'), import_replace_schema2 = require('./replace-schema.js'), import_utils4 = require('./utils.js'), import_error2 = require('./error.js'), import_env2 = require('./universal/env.js'), import_file = require('./universal/file.js'), import_system = require("@sinclair/typebox/system"), _a;
_a = Symbol.dispose;
const _Elysia = class _Elysia {
constructor(config = {}) {
this.server = null;
this.dependencies = {};
this["~Prefix"] = "";
this["~Singleton"] = null;
this["~Definitions"] = null;
this["~Metadata"] = null;
this["~Ephemeral"] = null;
this["~Volatile"] = null;
this["~Routes"] = null;
this.singleton = {
decorator: {},
store: {},
derive: {},
resolve: {}
};
this.definitions = {
typebox: import_type_system.t.Module({}),
type: {},
error: {}
};
this.extender = {
macro: {},
higherOrderFunctions: []
};
this.validator = {
global: null,
scoped: null,
local: null,
getCandidate() {
return !this.global && !this.scoped && !this.local ? {
body: void 0,
headers: void 0,
params: void 0,
query: void 0,
cookie: void 0,
response: void 0
} : (0, import_utils.mergeSchemaValidator)(
(0, import_utils.mergeSchemaValidator)(this.global, this.scoped),
this.local
);
}
};
this.standaloneValidator = {
global: null,
scoped: null,
local: null
};
this.event = {};
this.router = {
"~http": void 0,
get http() {
return this["~http"] || (this["~http"] = new import_memoirist.Memoirist({
lazy: !0,
onParam: import_fast_decode_uri_component.default
})), this["~http"];
},
"~dynamic": void 0,
// Use in non-AOT mode
get dynamic() {
return this["~dynamic"] || (this["~dynamic"] = new import_memoirist.Memoirist({
onParam: import_fast_decode_uri_component.default
})), this["~dynamic"];
},
// Static Router
static: {},
// Native Static Response
response: {},
history: []
};
this.routeTree = {};
this.inference = {
body: !1,
cookie: !1,
headers: !1,
query: !1,
set: !1,
server: !1,
path: !1,
route: !1,
url: !1
};
this["~parser"] = {};
this.handle = async (request) => this.fetch(request);
this.handleError = async (context, error) => (this.handleError = this.config.aot ? (0, import_compose.composeErrorHandler)(this) : (0, import_dynamic_handle.createDynamicErrorHandler)(this))(context, error);
/**
* ### listen
* Assign current instance to port and start serving
*
* ---
* @example
* ```typescript
* new Elysia()
* .get("/", () => 'hi')
* .listen(3000)
* ```
*/
this.listen = (options, callback) => (this["~adapter"].listen(this)(options, callback), this);
/**
* ### stop
* Stop server from serving
*
* ---
* @example
* ```typescript
* const app = new Elysia()
* .get("/", () => 'hi')
* .listen(3000)
*
* // Sometime later
* app.stop()
* ```
*
* @example
* ```typescript
* const app = new Elysia()
* .get("/", () => 'hi')
* .listen(3000)
*
* app.stop(true) // Abruptly any requests inflight
* ```
*/
this.stop = async (closeActiveConnections) => (await this["~adapter"].stop?.(this, closeActiveConnections), this);
this[_a] = () => {
this.server && this.stop();
};
config.tags && (config.detail ? config.detail.tags = config.tags : config.detail = {
tags: config.tags
}), this.config = {
aot: import_env.env.ELYSIA_AOT !== "false",
nativeStaticResponse: !0,
encodeSchema: !0,
normalize: !0,
...config,
prefix: config.prefix ? config.prefix.charCodeAt(0) === 47 ? config.prefix : `/${config.prefix}` : void 0,
cookie: {
path: "/",
...config?.cookie
},
experimental: config?.experimental ?? {},
seed: config?.seed === void 0 ? "" : config?.seed
}, this["~adapter"] = config.adapter ?? (typeof Bun < "u" ? import_bun.BunAdapter : import_web_standard.WebStandardAdapter), config?.analytic && (config?.name || config?.seed !== void 0) && (this.telemetry = {
stack: new Error().stack
});
}
get store() {
return this.singleton.store;
}
get decorator() {
return this.singleton.decorator;
}
get routes() {
return this.router.history;
}
getGlobalRoutes() {
return this.router.history;
}
getGlobalDefinitions() {
return this.definitions;
}
getServer() {
return this.server;
}
getParent() {
return null;
}
get promisedModules() {
return this._promisedModules || (this._promisedModules = new import_utils.PromiseGroup(console.error, () => {
})), this._promisedModules;
}
env(model, _env = import_env.env) {
if ((0, import_schema.getSchemaValidator)(model, {
modules: this.definitions.typebox,
dynamic: !0,
additionalProperties: !0,
coerce: !0,
sanitize: () => this.config.sanitize
}).Check(_env) === !1) {
const error = new import_error.ValidationError("env", model, _env);
throw new Error(error.all.map((x) => x.summary).join(`
`));
}
return this;
}
/**
* @private DO_NOT_USE_OR_YOU_WILL_BE_FIRED
* @version 1.1.0
*
* ! Do not use unless you know exactly what you are doing
* ? Add Higher order function to Elysia.fetch
*/
wrap(fn) {
return this.extender.higherOrderFunctions.push({
checksum: (0, import_utils2.checksum)(
JSON.stringify({
name: this.config.name,
seed: this.config.seed,
content: fn.toString()
})
),
fn
}), this;
}
get models() {
const models = {};
for (const name of Object.keys(this.definitions.type))
models[name] = (0, import_schema.getSchemaValidator)(
this.definitions.typebox.Import(name),
{
models: this.definitions.type
}
);
return models.modules = this.definitions.typebox, models;
}
add(method, path, handle, localHook, options) {
const skipPrefix = options?.skipPrefix ?? !1, allowMeta = options?.allowMeta ?? !1;
localHook ??= {}, this.applyMacro(localHook);
let standaloneValidators = [];
if (localHook.standaloneValidator && (standaloneValidators = standaloneValidators.concat(
localHook.standaloneValidator
)), this.standaloneValidator.local && (standaloneValidators = standaloneValidators.concat(
this.standaloneValidator.local
)), this.standaloneValidator.scoped && (standaloneValidators = standaloneValidators.concat(
this.standaloneValidator.scoped
)), this.standaloneValidator.global && (standaloneValidators = standaloneValidators.concat(
this.standaloneValidator.global
)), path !== "" && path.charCodeAt(0) !== 47 && (path = "/" + path), this.config.prefix && !skipPrefix && (path = this.config.prefix + path), localHook?.type)
switch (localHook.type) {
case "text":
localHook.type = "text/plain";
break;
case "json":
localHook.type = "application/json";
break;
case "formdata":
localHook.type = "multipart/form-data";
break;
case "urlencoded":
localHook.type = "application/x-www-form-urlencoded";
break;
case "arrayBuffer":
localHook.type = "application/octet-stream";
break;
default:
break;
}
const instanceValidator = this.validator.getCandidate(), cloned = {
body: localHook?.body ?? instanceValidator?.body,
headers: localHook?.headers ?? instanceValidator?.headers,
params: localHook?.params ?? instanceValidator?.params,
query: localHook?.query ?? instanceValidator?.query,
cookie: localHook?.cookie ?? instanceValidator?.cookie,
response: localHook?.response ?? instanceValidator?.response
}, shouldPrecompile = this.config.precompile === !0 || typeof this.config.precompile == "object" && this.config.precompile.compose === !0, createValidator = () => {
const models = this.definitions.type, dynamic = !this.config.aot, normalize = this.config.normalize, modules = this.definitions.typebox, sanitize = () => this.config.sanitize, cookieValidator = () => {
if (cloned.cookie || standaloneValidators.find((x) => x.cookie))
return (0, import_schema.getCookieValidator)({
modules,
validator: cloned.cookie,
defaultConfig: this.config.cookie,
normalize,
config: cloned.cookie?.config ?? {},
dynamic,
models,
validators: standaloneValidators.map((x) => x.cookie),
sanitize
});
};
return shouldPrecompile ? {
body: (0, import_schema.getSchemaValidator)(cloned.body, {
modules,
dynamic,
models,
normalize,
additionalCoerce: (() => {
const resolved = (0, import_schema.resolveSchema)(
cloned.body,
models,
modules
);
return resolved && import_typebox.Kind in resolved && ((0, import_schema.hasType)("File", resolved) || (0, import_schema.hasType)("Files", resolved)) ? (0, import_replace_schema.coerceFormData)() : (0, import_replace_schema.coercePrimitiveRoot)();
})(),
validators: standaloneValidators.map((x) => x.body),
sanitize
}),
headers: (0, import_schema.getSchemaValidator)(cloned.headers, {
modules,
dynamic,
models,
additionalProperties: !0,
coerce: !0,
additionalCoerce: (0, import_replace_schema.stringToStructureCoercions)(),
validators: standaloneValidators.map(
(x) => x.headers
),
sanitize
}),
params: (0, import_schema.getSchemaValidator)(cloned.params, {
modules,
dynamic,
models,
coerce: !0,
additionalCoerce: (0, import_replace_schema.stringToStructureCoercions)(),
validators: standaloneValidators.map(
(x) => x.params
),
sanitize
}),
query: (0, import_schema.getSchemaValidator)(cloned.query, {
modules,
dynamic,
models,
normalize,
coerce: !0,
additionalCoerce: (0, import_replace_schema.queryCoercions)(),
validators: standaloneValidators.map(
(x) => x.query
),
sanitize
}),
cookie: cookieValidator(),
response: (0, import_schema.getResponseSchemaValidator)(cloned.response, {
modules,
dynamic,
models,
normalize,
validators: standaloneValidators.map(
(x) => x.response
),
sanitize
})
} : {
createBody() {
return this.body ? this.body : this.body = (0, import_schema.getSchemaValidator)(
cloned.body,
{
modules,
dynamic,
models,
normalize,
additionalCoerce: (() => {
const resolved = (0, import_schema.resolveSchema)(
cloned.body,
models,
modules
);
return resolved && import_typebox.Kind in resolved && ((0, import_schema.hasType)("File", resolved) || (0, import_schema.hasType)("Files", resolved)) ? (0, import_replace_schema.coerceFormData)() : (0, import_replace_schema.coercePrimitiveRoot)();
})(),
validators: standaloneValidators.map(
(x) => x.body
),
sanitize
}
);
},
createHeaders() {
return this.headers ? this.headers : this.headers = (0, import_schema.getSchemaValidator)(
cloned.headers,
{
modules,
dynamic,
models,
normalize,
additionalProperties: !normalize,
coerce: !0,
additionalCoerce: (0, import_replace_schema.stringToStructureCoercions)(),
validators: standaloneValidators.map(
(x) => x.headers
),
sanitize
}
);
},
createParams() {
return this.params ? this.params : this.params = (0, import_schema.getSchemaValidator)(
cloned.params,
{
modules,
dynamic,
models,
normalize,
coerce: !0,
additionalCoerce: (0, import_replace_schema.stringToStructureCoercions)(),
validators: standaloneValidators.map(
(x) => x.params
),
sanitize
}
);
},
createQuery() {
return this.query ? this.query : this.query = (0, import_schema.getSchemaValidator)(
cloned.query,
{
modules,
dynamic,
models,
normalize,
coerce: !0,
additionalCoerce: (0, import_replace_schema.queryCoercions)(),
validators: standaloneValidators.map(
(x) => x.query
),
sanitize
}
);
},
createCookie() {
return this.cookie ? this.cookie : this.cookie = cookieValidator();
},
createResponse() {
return this.response ? this.response : this.response = (0, import_schema.getResponseSchemaValidator)(
cloned.response,
{
modules,
dynamic,
models,
normalize,
validators: standaloneValidators.map(
(x) => x.response
),
sanitize
}
);
}
};
};
(instanceValidator.body || instanceValidator.cookie || instanceValidator.headers || instanceValidator.params || instanceValidator.query || instanceValidator.response) && (localHook = (0, import_utils2.mergeHook)(localHook, instanceValidator)), localHook.tags && (localHook.detail ? localHook.detail.tags = localHook.tags : localHook.detail = {
tags: localHook.tags
}), (0, import_utils.isNotEmpty)(this.config.detail) && (localHook.detail = (0, import_utils.mergeDeep)(
Object.assign({}, this.config.detail),
localHook.detail
));
const hooks = (0, import_utils.isNotEmpty)(this.event) ? (0, import_utils2.mergeHook)(this.event, (0, import_utils.localHookToLifeCycleStore)(localHook)) : { ...(0, import_utils.lifeCycleToArray)((0, import_utils.localHookToLifeCycleStore)(localHook)) };
if (standaloneValidators.length && Object.assign(hooks, {
standaloneValidator: standaloneValidators
}), this.config.aot === !1) {
const validator = createValidator();
this.router.dynamic.add(method, path, {
validator,
hooks,
content: localHook?.type,
handle,
route: path
});
const encoded = (0, import_utils.encodePath)(path, { dynamic: !0 });
if (path !== encoded && this.router.dynamic.add(method, encoded, {
validator,
hooks,
content: localHook?.type,
handle,
route: path
}), !this.config.strictPath) {
const loosePath = (0, import_utils.getLoosePath)(path);
this.router.dynamic.add(method, loosePath, {
validator,
hooks,
content: localHook?.type,
handle,
route: path
});
const encoded2 = (0, import_utils.encodePath)(loosePath);
loosePath !== encoded2 && this.router.dynamic.add(method, loosePath, {
validator,
hooks,
content: localHook?.type,
handle,
route: path
});
}
this.router.history.push({
method,
path,
composed: null,
handler: handle,
compile: void 0,
hooks
});
return;
}
const adapter = this["~adapter"].handler, nativeStaticHandler = typeof handle != "function" ? () => {
const context = {
redirect: import_utils.redirect,
request: this["~adapter"].isWebStandard ? new Request(`http://ely.sia${path}`, {
method
}) : void 0,
server: null,
set: {
headers: Object.assign({}, this.setHeaders)
},
status: import_error.status,
store: this.store
};
try {
this.event.request?.map((x) => {
if (typeof x.fn == "function")
return x.fn(context);
if (typeof x == "function") return x(context);
});
} catch (error) {
let res;
context.error = error, this.event.error?.some((x) => {
if (typeof x.fn == "function")
return res = x.fn(context);
if (typeof x == "function")
return res = x(context);
}), res !== void 0 && (handle = res);
}
const fn = adapter.createNativeStaticHandler?.(
handle,
hooks,
context.set
);
return fn instanceof Promise ? fn.then((fn2) => {
if (fn2) return fn2;
}) : fn?.();
} : void 0, useNativeStaticResponse = this.config.nativeStaticResponse === !0, addResponsePath = (path2) => {
!useNativeStaticResponse || !nativeStaticHandler || (import_utils.supportPerMethodInlineHandler ? this.router.response[path2] ? this.router.response[path2][method] = nativeStaticHandler() : this.router.response[path2] = {
[method]: nativeStaticHandler()
} : this.router.response[path2] = nativeStaticHandler());
};
addResponsePath(path);
let _compiled;
const compile = () => {
if (_compiled) return _compiled;
const compiled = (0, import_compose.composeHandler)({
app: this,
path,
method,
hooks,
validator: createValidator(),
handler: typeof handle != "function" && typeof adapter.createStaticHandler != "function" ? () => handle : handle,
allowMeta,
inference: this.inference
});
return this.router.history[index] && (_compiled = this.router.history[index].composed = compiled), compiled;
};
let oldIndex;
if (`${method}_${path}` in this.routeTree)
for (let i = 0; i < this.router.history.length; i++) {
const route2 = this.router.history[i];
if (route2.path === path && route2.method === method) {
oldIndex = i;
break;
}
}
else this.routeTree[`${method}_${path}`] = this.router.history.length;
const index = oldIndex ?? this.router.history.length, route = this.router.history, mainHandler = shouldPrecompile ? compile() : (ctx) => _compiled ? _compiled(ctx) : (route[index].composed = compile())(ctx);
oldIndex !== void 0 ? this.router.history[oldIndex] = Object.assign(
{
method,
path,
composed: mainHandler,
compile,
handler: handle,
hooks
},
standaloneValidators.length ? {
standaloneValidators
} : void 0,
localHook.webSocket ? { websocket: localHook.websocket } : void 0
) : this.router.history.push(
Object.assign(
{
method,
path,
composed: mainHandler,
compile,
handler: handle,
hooks
},
localHook.webSocket ? { websocket: localHook.websocket } : void 0
)
);
const handler = {
handler: shouldPrecompile ? route[index].composed : void 0,
compile() {
return this.handler = compile();
}
}, staticRouter = this.router.static, isStaticPath = path.indexOf(":") === -1 && path.indexOf("*") === -1;
if (method === "WS") {
if (isStaticPath) {
path in staticRouter ? staticRouter[path][method] = index : staticRouter[path] = {
[method]: index
};
return;
}
this.router.http.add("WS", path, handler), this.config.strictPath || this.router.http.add("WS", (0, import_utils.getLoosePath)(path), handler);
const encoded = (0, import_utils.encodePath)(path, { dynamic: !0 });
path !== encoded && this.router.http.add("WS", encoded, handler);
return;
}
if (isStaticPath)
path in staticRouter ? staticRouter[path][method] = index : staticRouter[path] = {
[method]: index
}, this.config.strictPath || addResponsePath((0, import_utils.getLoosePath)(path));
else {
if (this.router.http.add(method, path, handler), !this.config.strictPath) {
const loosePath = (0, import_utils.getLoosePath)(path);
addResponsePath(loosePath), this.router.http.add(method, loosePath, handler);
}
const encoded = (0, import_utils.encodePath)(path, { dynamic: !0 });
path !== encoded && (this.router.http.add(method, encoded, handler), addResponsePath(encoded));
}
}
headers(header) {
return header ? (this.setHeaders || (this.setHeaders = {}), this.setHeaders = (0, import_utils.mergeDeep)(this.setHeaders, header), this) : this;
}
/**
* ### start | Life cycle event
* Called after server is ready for serving
*
* ---
* @example
* ```typescript
* new Elysia()
* .onStart(({ server }) => {
* console.log("Running at ${server?.url}:${server?.port}")
* })
* .listen(3000)
* ```
*/
onStart(handler) {
return this.on("start", handler), this;
}
onRequest(handler) {
return this.on("request", handler), this;
}
onParse(options, handler) {
return handler ? this.on(
options,
"parse",
handler
) : typeof options == "string" ? this.on("parse", this["~parser"][options]) : this.on("parse", options);
}
/**
* ### parse | Life cycle event
* Callback function to handle body parsing
*
* If truthy value is returned, will be assigned to `context.body`
* Otherwise will skip the callback and look for the next one.
*
* Equivalent to Express's body parser
*
* ---
* @example
* ```typescript
* new Elysia()
* .onParse((request, contentType) => {
* if(contentType === "application/json")
* return request.json()
* })
* ```
*/
parser(name, parser) {
return this["~parser"][name] = parser, this;
}
onTransform(options, handler) {
return handler ? this.on(
options,
"transform",
handler
) : this.on("transform", options);
}
resolve(optionsOrResolve, resolve) {
resolve || (resolve = optionsOrResolve, optionsOrResolve = { as: "local" });
const hook = {
subType: "resolve",
fn: resolve
};
return this.onBeforeHandle(optionsOrResolve, hook);
}
mapResolve(optionsOrResolve, mapper) {
mapper || (mapper = optionsOrResolve, optionsOrResolve = { as: "local" });
const hook = {
subType: "mapResolve",
fn: mapper
};
return this.onBeforeHandle(optionsOrResolve, hook);
}
onBeforeHandle(options, handler) {
return handler ? this.on(
options,
"beforeHandle",
handler
) : this.on("beforeHandle", options);
}
onAfterHandle(options, handler) {
return handler ? this.on(
options,
"afterHandle",
handler
) : this.on("afterHandle", options);
}
mapResponse(options, handler) {
return handler ? this.on(
options,
"mapResponse",
handler
) : this.on("mapResponse", options);
}
onAfterResponse(options, handler) {
return handler ? this.on(
options,
"afterResponse",
handler
) : this.on("afterResponse", options);
}
/**
* ### After Handle | Life cycle event
* Intercept request **after** main handler is called.
*
* If truthy value is returned, will be assigned as `Response`
*
* ---
* @example
* ```typescript
* new Elysia()
* .onAfterHandle((context, response) => {
* if(typeof response === "object")
* return JSON.stringify(response)
* })
* ```
*/
trace(options, handler) {
handler || (handler = options, options = { as: "local" }), Array.isArray(handler) || (handler = [handler]);
for (const fn of handler)
this.on(
options,
"trace",
(0, import_trace.createTracer)(fn)
);
return this;
}
error(name, error) {
switch (typeof name) {
case "string":
return error.prototype[import_error.ERROR_CODE] = name, this.definitions.error[name] = error, this;
case "function":
return this.definitions.error = name(this.definitions.error), this;
}
for (const [code, error2] of Object.entries(name))
error2.prototype[import_error.ERROR_CODE] = code, this.definitions.error[code] = error2;
return this;
}
/**
* ### Error | Life cycle event
* Called when error is thrown during processing request
*
* ---
* @example
* ```typescript
* new Elysia()
* .onError(({ code }) => {
* if(code === "NOT_FOUND")
* return "Path not found :("
* })
* ```
*/
onError(options, handler) {
return handler ? this.on(
options,
"error",
handler
) : this.on("error", options);
}
/**
* ### stop | Life cycle event
* Called after server stop serving request
*
* ---
* @example
* ```typescript
* new Elysia()
* .onStop((app) => {
* cleanup()
* })
* ```
*/
onStop(handler) {
return this.on("stop", handler), this;
}
on(optionsOrType, typeOrHandlers, handlers) {
let type;
switch (typeof optionsOrType) {
case "string":
type = optionsOrType, handlers = typeOrHandlers;
break;
case "object":
type = typeOrHandlers, !Array.isArray(typeOrHandlers) && typeof typeOrHandlers == "object" && (handlers = typeOrHandlers);
break;
}
Array.isArray(handlers) ? handlers = (0, import_utils.fnToContainer)(handlers) : typeof handlers == "function" ? handlers = [
{
fn: handlers
}
] : handlers = [handlers];
const handles = handlers;
for (const handle of handles)
handle.scope = typeof optionsOrType == "string" ? "local" : optionsOrType?.as ?? "local", (type === "resolve" || type === "derive") && (handle.subType = type);
type !== "trace" && (this.inference = (0, import_sucrose.sucrose)(
{
[type]: handles.map((x) => x.fn)
},
this.inference,
this.config.sucrose
));
for (const handle of handles) {
const fn = (0, import_utils2.asHookType)(handle, "global", { skipIfHasType: !0 });
switch (type) {
case "start":
this.event.start ??= [], this.event.start.push(fn);
break;
case "request":
this.event.request ??= [], this.event.request.push(fn);
break;
case "parse":
this.event.parse ??= [], this.event.parse.push(fn);
break;
case "transform":
this.event.transform ??= [], this.event.transform.push(fn);
break;
// @ts-expect-error
case "derive":
this.event.transform ??= [], this.event.transform.push(
(0, import_utils.fnToContainer)(fn, "derive")
);
break;
case "beforeHandle":
this.event.beforeHandle ??= [], this.event.beforeHandle.push(fn);
break;
// @ts-expect-error
// eslint-disable-next-line sonarjs/no-duplicated-branches
case "resolve":
this.event.beforeHandle ??= [], this.event.beforeHandle.push(
(0, import_utils.fnToContainer)(fn, "resolve")
);
break;
case "afterHandle":
this.event.afterHandle ??= [], this.event.afterHandle.push(fn);
break;
case "mapResponse":
this.event.mapResponse ??= [], this.event.mapResponse.push(fn);
break;
case "afterResponse":
this.event.afterResponse ??= [], this.event.afterResponse.push(fn);
break;
case "trace":
this.event.trace ??= [], this.event.trace.push(fn);
break;
case "error":
this.event.error ??= [], this.event.error.push(fn);
break;
case "stop":
this.event.stop ??= [], this.event.stop.push(fn);
break;
}
}
return this;
}
as(type) {
return (0, import_utils.promoteEvent)(this.event.parse, type), (0, import_utils.promoteEvent)(this.event.transform, type), (0, import_utils.promoteEvent)(this.event.beforeHandle, type), (0, import_utils.promoteEvent)(this.event.afterHandle, type), (0, import_utils.promoteEvent)(this.event.mapResponse, type), (0, import_utils.promoteEvent)(this.event.afterResponse, type), (0, import_utils.promoteEvent)(this.event.trace, type), (0, import_utils.promoteEvent)(this.event.error, type), type === "scoped" ? (this.validator.scoped = (0, import_utils.mergeSchemaValidator)(
this.validator.scoped,
this.validator.local
), this.validator.local = null, this.standaloneValidator.local !== null && (this.standaloneValidator.scoped ||= [], this.standaloneValidator.scoped.push(
...this.standaloneValidator.local
), this.standaloneValidator.local = null)) : type === "global" && (this.validator.global = (0, import_utils.mergeSchemaValidator)(
this.validator.global,
(0, import_utils.mergeSchemaValidator)(
this.validator.scoped,
this.validator.local
)
), this.validator.scoped = null, this.validator.local = null, this.standaloneValidator.local !== null && (this.standaloneValidator.scoped ||= [], this.standaloneValidator.scoped.push(
...this.standaloneValidator.local
), this.standaloneValidator.local = null), this.standaloneValidator.scoped !== null && (this.standaloneValidator.global ||= [], this.standaloneValidator.global.push(
...this.standaloneValidator.scoped
), this.standaloneValidator.scoped = null)), this;
}
/**
* ### group
* Encapsulate and group path with prefix
*
* ---
* @example
* ```typescript
* new Elysia()
* .group('/v1', app => app
* .get('/', () => 'Hi')
* .get('/name', () => 'Elysia')
* })
* ```
*/
group(prefix, schemaOrRun, run) {
const instance = new _Elysia({
...this.config,
prefix: ""
});
instance.singleton = { ...this.singleton }, instance.definitions = { ...this.definitions }, instance.getServer = () => this.getServer(), instance.inference = (0, import_utils.cloneInference)(this.inference), instance.extender = { ...this.extender }, instance["~parser"] = this["~parser"], instance.standaloneValidator = {
local: [...this.standaloneValidator.local ?? []],
scoped: [...this.standaloneValidator.scoped ?? []],
global: [...this.standaloneValidator.global ?? []]
};
const isSchema = typeof schemaOrRun == "object", sandbox = (isSchema ? run : schemaOrRun)(instance);
return this.singleton = (0, import_utils.mergeDeep)(this.singleton, instance.singleton), this.definitions = (0, import_utils.mergeDeep)(this.definitions, instance.definitions), sandbox.event.request?.length && (this.event.request = [
...this.event.request || [],
...sandbox.event.request || []
]), sandbox.event.mapResponse?.length && (this.event.mapResponse = [
...this.event.mapResponse || [],
...sandbox.event.mapResponse || []
]), this.model(sandbox.definitions.type), Object.values(instance.router.history).forEach(
({ method, path, handler, hooks }) => {
if (path = (isSchema ? "" : this.config.prefix ?? "") + prefix + path, isSchema) {
const {
body,
headers,
query,
params,
cookie,
response,
...hook
} = schemaOrRun, localHook = hooks;
this.applyMacro(hook);
const hasStandaloneSchema = body || headers || query || params || cookie || response;
this.add(
method,
path,
handler,
(0, import_utils2.mergeHook)(hook, {
...localHook || {},
error: localHook.error ? Array.isArray(localHook.error) ? [
...localHook.error ?? [],
...sandbox.event.error ?? []
] : [
localHook.error,
...sandbox.event.error ?? []
] : sandbox.event.error,
// Merge macro's standaloneValidator with local and group schema
standaloneValidator: hook.standaloneValidator || localHook.standaloneValidator || hasStandaloneSchema ? [
...hook.standaloneValidator ?? [],
...localHook.standaloneValidator ?? [],
...hasStandaloneSchema ? [
{
body,
headers,
query,
params,
cookie,
response
}
] : []
] : void 0
}),
void 0
);
} else
this.add(
method,
path,
handler,
(0, import_utils2.mergeHook)(hooks, {
error: sandbox.event.error
}),
{
skipPrefix: !0
}
);
}
), this;
}
/**
* ### guard
* Encapsulate and pass hook into all child handler
*
* ---
* @example
* ```typescript
* import { t } from 'elysia'
*
* new Elysia()
* .guard({
* body: t.Object({
* username: t.String(),
* password: t.String()
* })
* }, app => app
* .get("/", () => 'Hi')
* .get("/name", () => 'Elysia')
* })
* ```
*/
guard(hook, run) {
if (!run) {
if (typeof hook == "object") {
this.applyMacro(hook), hook.detail && (this.config.detail ? this.config.detail = (0, import_utils.mergeDeep)(
Object.assign({}, this.config.detail),
hook.detail
) : this.config.detail = hook.detail), hook.tags && (this.config.detail ? this.config.detail.tags = hook.tags : this.config.detail = {
tags: hook.tags
});
const type = hook.as ?? "local";
if (hook.schema === "standalone") {
this.standaloneValidator[type] || (this.standaloneValidator[type] = []);
const response = hook?.response ? typeof hook.response == "string" || import_typebox.Kind in hook.response || "~standard" in hook.response ? {
200: hook.response
} : hook?.response : void 0;
this.standaloneValidator[type].push({
body: hook.body,
headers: hook.headers,
params: hook.params,
query: hook.query,
response,
cookie: hook.cookie
});
} else
this.validator[type] = {
body: hook.body ?? this.validator[type]?.body,
headers: hook.headers ?? this.validator[type]?.headers,
params: hook.params ?? this.validator[type]?.params,
query: hook.query ?? this.validator[type]?.query,
response: hook.response ?? this.validator[type]?.response,
cookie: hook.cookie ?? this.validator[type]?.cookie
};
return hook.parse && this.on({ as: type }, "parse", hook.parse), hook.transform && this.on({ as: type }, "transform", hook.transform), hook.derive && this.on({ as: type }, "derive", hook.derive), hook.beforeHandle && this.on({ as: type }, "beforeHandle", hook.beforeHandle), hook.resolve && this.on({ as: type }, "resolve", hook.resolve), hook.afterHandle && this.on({ as: type }, "afterHandle", hook.afterHandle), hook.mapResponse && this.on({ as: type }, "mapResponse", hook.mapResponse), hook.afterResponse && this.on({ as: type }, "afterResponse", hook.afterResponse), hook.error && this.on({ as: type }, "error", hook.error), this;
}
return this.guard({}, hook);
}
const instance = new _Elysia({
...this.config,
prefix: ""
});
instance.singleton = { ...this.singleton }, instance.definitions = { ...this.definitions }, instance.inference = (0, import_utils.cloneInference)(this.inference), instance.extender = { ...this.extender }, instance.getServer = () => this.getServer();
const sandbox = run(instance);
return this.singleton = (0, import_utils.mergeDeep)(this.singleton, instance.singleton), this.definitions = (0, import_utils.mergeDeep)(this.definitions, instance.definitions), sandbox.getServer = () => this.server, sandbox.event.request?.length && (this.event.request = [
...this.event.request || [],
...sandbox.event.request || []
]), sandbox.event.mapResponse?.length && (this.event.mapResponse = [
...this.event.mapResponse || [],
...sandbox.event.mapResponse || []
]), this.model(sandbox.definitions.type), Object.values(instance.router.history).forEach(
({ method, path, handler, hooks: localHook }) => {
const {
body,
headers,
query,
params,
cookie,
response,
...guardHook
} = hook, hasStandaloneSchema = body || headers || query || params || cookie || response;
this.add(
method,
path,
handler,
(0, import_utils2.mergeHook)(guardHook, {
...localHook || {},
error: localHook.error ? Array.isArray(localHook.error) ? [
...localHook.error ?? [],
...sandbox.event.error ?? []
] : [
localHook.error,
...sandbox.event.error ?? []
] : sandbox.event.error,
standaloneValidator: hasStandaloneSchema ? [
...localHook.standaloneValidator ?? [],
{
body,
headers,
query,
params,
cookie,
response
}
] : localHook.standaloneValidator
})
);
}
), this;
}
/**
* ### use
* Merge separate logic of Elysia with current
*
* ---
* @example
* ```typescript
* const plugin = (app: Elysia) => app
* .get('/plugin', () => 'hi')
*
* new Elysia()
* .use(plugin)
* ```
*/
use(plugin) {
if (!plugin) return this;
if (Array.isArray(plugin)) {
let app = this;
for (const p of plugin) app = app.use(p);
return app;
}
return plugin instanceof Promise ? (this.promisedModules.add(
plugin.then((plugin2) => {
if (typeof plugin2 == "function") return plugin2(this);
if (plugin2 instanceof _Elysia)
return this._use(plugin2).compile();
if (plugin2.constructor?.name === "Elysia")
return this._use(
plugin2
).compile();
if (typeof plugin2.default == "function")
return plugin2.default(this);
if (plugin2.default instanceof _Elysia)
return this._use(plugin2.default);
if (plugin2.constructor?.name === "Elysia")
return this._use(plugin2.default);
if (plugin2.constructor?.name === "_Elysia")
return this._use(plugin2.default);
try {
return this._use(plugin2.default);
} catch (error) {
throw console.error(
'Invalid plugin type. Expected Elysia instance, function, or module with "default" as Elysia instance or function that returns Elysia instance.'
), error;
}
}).then((v) => (v && typeof v.compile == "function" && v.compile(), v))
), this) : this._use(plugin);
}
propagatePromiseModules(plugin) {
if (plugin.promisedModules.size <= 0) return this;
for (const promise of plugin.promisedModules.promises)
this.promisedModules.add(
promise.then((v) => {
if (!v) return;
const t3 = this._use(v);
return t3 instanceof Promise ? t3.then((v2) => {
v2 ? v2.compile() : v.compile();
}) : v.compile();
})
);
return this;
}
_use(plugin) {
if (typeof plugin == "function") {
const instance = plugin(this);
return instance instanceof Promise ? (this.promisedModules.add(
instance.then((plugin2) => {
if (plugin2 instanceof _Elysia) {
plugin2.getServer = () => this.getServer(), plugin2.getGlobalRoutes = () => this.getGlobalRoutes(), plugin2.getGlobalDefinitions = () => this.getGlobalDefinitions(), plugin2.model(this.definitions.type), plugin2.error(this.definitions.error);
for (const {
method,
path,
handler,
hooks
} of Object.values(plugin2.router.history))
this.add(
method,
path,
handler,
hooks,
void 0
);
return plugin2 === this ? void 0 : (this.propagatePromiseModules(plugin2), plugin2);
}
return typeof plugin2 == "function" ? plugin2(
this
) : typeof plugin2.default == "function" ? plugin2.default(
this
) : this._use(plugin2);
}).then((v) => (v && typeof v.compile == "function" && v.compile(), v))
), this) : instance;
}
this.propagatePromiseModules(plugin);
const name = plugin.config.name, seed = plugin.config.seed;
if (plugin.getParent = () => this, plugin.getServer = () => this.getServer(), plugin.getGlobalRoutes = () => this.getGlobalRoutes(), plugin.getGlobalDefinitions = () => this.getGlobalDefinitions(), plugin.standaloneValidator?.scoped && (this.standaloneValidator.local ? this.standaloneValidator.local = this.standaloneValidator.local.concat(
plugin.standaloneValidator.scoped
) : this.standaloneValidator.local = plugin.standaloneValidator.scoped), plugin.standaloneValidator?.global && (this.standaloneValidator.global ? this.standaloneValidator.global = this.standaloneValidator.global.concat(
plugin.standaloneValidator.global
) : this.standaloneValidator.global = plugin.standaloneValidator.global), (0, import_utils.isNotEmpty)(plugin["~parser"]) && (this["~parser"] = {
...plugin["~parser"],
...this["~parser"]
}), plugin.setHeaders && this.headers(plugin.setHeaders), name) {
name in this.dependencies || (this.dependencies[name] = []);
const current = seed !== void 0 ? (0, import_utils2.checksum)(name + JSON.stringify(seed)) : 0;
this.dependencies[name].some(
({ checksum: checksum3 }) => current === checksum3
) || (this.extender.macro = {
...this.extender.macro,
...plugin.extender.macro
}, this.extender.higherOrderFunctions = this.extender.higherOrderFunctions.concat(
plugin.extender.higherOrderFunctions
));
} else
(0, import_utils.isNotEmpty)(plugin.extender.macro) && (this.extender.macro = {
...this.extender.macro,
...plugin.extender.macro
}), plugin.extender.higherOrderFunctions.length && (this.extender.higherOrderFunctions = this.extender.higherOrderFunctions.concat(
plugin.extender.higherOrderFunctions
));
if (plugin.extender.higherOrderFunctions.length) {
(0, import_utils.deduplicateChecksum)(this.extender.higherOrderFunctions);
const hofHashes = [];
for (let i = 0; i < this.extender.higherOrderFunctions.length; i++) {
const hof = this.extender.higherOrderFunctions[i];
hof.checksum && (hofHashes.includes(hof.checksum) && (this.extender.higherOrderFunctions.splice(i, 1), i--), hofHashes.push(hof.checksum));
}
hofHashes.length = 0;
}
this.inference = (0, import_sucrose.mergeInference)(this.inference, plugin.inference), (0, import_utils.isNotEmpty)(plugin.singleton.decorator) && this.decorate(plugin.singleton.decorator), (0, import_utils.isNotEmpty)(plugin.singleton.store) && this.state(plugin.singleton.store), (0, import_utils.isNotEmpty)(plugin.definitions.type) && this.model(plugin.definitions.type), (0, import_utils.isNotEmpty)(plugin.definitions.error) && this.error(plugin.definitions.error), (0, import_utils.isNotEmpty)(plugin.extender.macro) && (this.extender.macro = {
...this.extender.macro,
...plugin.extender.macro
});
for (const { method, path, handler, hooks } of Object.values(
plugin.router.history
))
this.add(method, path, handler, hooks);
if (name) {
name in this.dependencies || (this.dependencies[name] = []);
const current = seed !== void 0 ? (0, import_utils2.checksum)(name + JSON.stringify(seed)) : 0;
if (this.dependencies[name].some(
({ checksum: checksum3 }) => current === checksum3
))
return this;
this.dependencies[name].push(
this.config?.analytic ? {
name: plugin.config.name,
seed: plugin.config.seed,
checksum: current,