UNPKG

elysia

Version:

Ergonomic Framework for Human

696 lines (695 loc) 25 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: !0 }), mod); var utils_exports = {}; __export(utils_exports, { ELYSIA_FORM_DATA: () => ELYSIA_FORM_DATA, ELYSIA_REQUEST_ID: () => ELYSIA_REQUEST_ID, InvertedStatusMap: () => InvertedStatusMap, PromiseGroup: () => PromiseGroup, StatusMap: () => StatusMap, asHookType: () => asHookType, checksum: () => checksum, cloneInference: () => cloneInference, deduplicateChecksum: () => deduplicateChecksum, deepClone: () => deepClone, emptySchema: () => emptySchema, encodePath: () => encodePath, filterGlobalHook: () => filterGlobalHook, fnToContainer: () => fnToContainer, form: () => form, getLoosePath: () => getLoosePath, getResponseLength: () => getResponseLength, hasHeaderShorthand: () => hasHeaderShorthand, hasSetImmediate: () => hasSetImmediate, injectChecksum: () => injectChecksum, insertStandaloneValidator: () => insertStandaloneValidator, isClass: () => isClass, isNotEmpty: () => isNotEmpty, isNumericString: () => isNumericString, lifeCycleToArray: () => lifeCycleToArray, lifeCycleToFn: () => lifeCycleToFn, localHookToLifeCycleStore: () => localHookToLifeCycleStore, mergeCookie: () => mergeCookie, mergeDeep: () => mergeDeep, mergeHook: () => mergeHook, mergeLifeCycle: () => mergeLifeCycle, mergeObjectArray: () => mergeObjectArray, mergeResponse: () => mergeResponse, mergeSchemaValidator: () => mergeSchemaValidator, primitiveHooks: () => primitiveHooks, promoteEvent: () => promoteEvent, randomId: () => randomId, redirect: () => redirect, replaceUrlPath: () => replaceUrlPath, signCookie: () => signCookie, sse: () => sse, supportPerMethodInlineHandler: () => supportPerMethodInlineHandler, unsignCookie: () => unsignCookie }); module.exports = __toCommonJS(utils_exports); var import_file = require('./universal/file.js'); const hasHeaderShorthand = "toJSON" in new Headers(), replaceUrlPath = (url, pathname) => { const urlObject = new URL(url); return urlObject.pathname = pathname, urlObject.toString(); }, isClass = (v) => typeof v == "function" && /^\s*class\s+/.test(v.toString()) || // Handle Object.create(null) v.toString && // Handle import * as Sentry from '@sentry/bun' // This also handle [object Date], [object Array] // and FFI value like [object Prisma] v.toString().startsWith("[object ") && v.toString() !== "[object Object]" || // If object prototype is not pure, then probably a class-like object isNotEmpty(Object.getPrototypeOf(v)), isObject = (item) => item && typeof item == "object" && !Array.isArray(item), mergeDeep = (target, source, options) => { const skipKeys = options?.skipKeys, override = options?.override ?? !0, mergeArray = options?.mergeArray ?? !1, seen = options?.seen ?? /* @__PURE__ */ new WeakSet(); if (!isObject(target) || !isObject(source) || seen.has(source)) return target; seen.add(source); for (const [key, value] of Object.entries(source)) if (!(skipKeys?.includes(key) || ["__proto__", "constructor", "prototype"].includes(key))) { if (mergeArray && Array.isArray(value)) { target[key] = Array.isArray( target[key] ) ? [...target[key], ...value] : target[key] = value; continue; } if (!isObject(value) || !(key in target) || isClass(value)) { if ((override || !(key in target)) && !Object.isFrozen(target)) try { target[key] = value; } catch { } continue; } if (!Object.isFrozen(target[key])) try { target[key] = mergeDeep( target[key], value, { skipKeys, override, mergeArray, seen } ); } catch { } } return seen.delete(source), target; }, mergeCookie = (a, b) => { const v = mergeDeep(Object.assign({}, a), b, { skipKeys: ["properties"], mergeArray: !1 }); return v.properties && delete v.properties, v; }, mergeObjectArray = (a, b) => { if (!b) return a; const array = [], checksums = []; if (a) { Array.isArray(a) || (a = [a]); for (const item of a) array.push(item), item.checksum && checksums.push(item.checksum); } if (b) { Array.isArray(b) || (b = [b]); for (const item of b) checksums.includes(item.checksum) || array.push(item); } return array; }, primitiveHooks = [ "start", "request", "parse", "transform", "resolve", "beforeHandle", "afterHandle", "mapResponse", "afterResponse", "trace", "error", "stop", "body", "headers", "params", "query", "response", "type", "detail" ], primitiveHookMap = primitiveHooks.reduce( (acc, x) => (acc[x] = !0, acc), {} ), isRecordNumber = (x) => typeof x == "object" && Object.keys(x).every((x2) => !isNaN(+x2)), mergeResponse = (a, b) => isRecordNumber(a) && isRecordNumber(b) ? Object.assign({}, a, b) : a && !isRecordNumber(a) && isRecordNumber(b) ? Object.assign({ 200: a }, b) : b ?? a, mergeSchemaValidator = (a, b) => !a && !b ? { body: void 0, headers: void 0, params: void 0, query: void 0, cookie: void 0, response: void 0 } : { body: b?.body ?? a?.body, headers: b?.headers ?? a?.headers, params: b?.params ?? a?.params, query: b?.query ?? a?.query, cookie: b?.cookie ?? a?.cookie, // @ts-ignore ? This order is correct - SaltyAom response: mergeResponse( // @ts-ignore a?.response, // @ts-ignore b?.response ) }, mergeHook = (a, b) => { if (!b) return a ?? {}; if (!a) return b ?? {}; if (!Object.values(b).find((x) => x != null)) return { ...a }; const hook = { ...a, ...b, // Merge local hook first // @ts-ignore body: b.body ?? a.body, // @ts-ignore headers: b.headers ?? a.headers, // @ts-ignore params: b.params ?? a.params, // @ts-ignore query: b.query ?? a.query, // @ts-ignore cookie: b.cookie ?? a.cookie, // ? This order is correct - SaltyAom response: mergeResponse( // @ts-ignore a.response, // @ts-ignore b.response ), type: a.type || b.type, detail: mergeDeep( // @ts-ignore b.detail ?? {}, // @ts-ignore a.detail ?? {} ), parse: mergeObjectArray(a.parse, b.parse), transform: mergeObjectArray(a.transform, b.transform), beforeHandle: mergeObjectArray( mergeObjectArray( // @ts-ignore fnToContainer(a.resolve, "resolve"), a.beforeHandle ), mergeObjectArray( fnToContainer(b.resolve, "resolve"), b.beforeHandle ) ), afterHandle: mergeObjectArray(a.afterHandle, b.afterHandle), mapResponse: mergeObjectArray(a.mapResponse, b.mapResponse), afterResponse: mergeObjectArray( a.afterResponse, b.afterResponse ), trace: mergeObjectArray(a.trace, b.trace), error: mergeObjectArray(a.error, b.error), // @ts-ignore standaloneSchema: ( // @ts-ignore a.standaloneSchema || b.standaloneSchema ? ( // @ts-ignore a.standaloneSchema && !b.standaloneSchema ? ( // @ts-ignore a.standaloneSchema ) : ( // @ts-ignore b.standaloneSchema && !a.standaloneSchema ? b.standaloneSchema : [ // @ts-ignore ...a.standaloneSchema ?? [], ...b.standaloneSchema ?? [] ] ) ) : void 0 ) }; return hook.resolve && delete hook.resolve, hook; }, lifeCycleToArray = (a) => { a.parse && !Array.isArray(a.parse) && (a.parse = [a.parse]), a.transform && !Array.isArray(a.transform) && (a.transform = [a.transform]), a.afterHandle && !Array.isArray(a.afterHandle) && (a.afterHandle = [a.afterHandle]), a.mapResponse && !Array.isArray(a.mapResponse) && (a.mapResponse = [a.mapResponse]), a.afterResponse && !Array.isArray(a.afterResponse) && (a.afterResponse = [a.afterResponse]), a.trace && !Array.isArray(a.trace) && (a.trace = [a.trace]), a.error && !Array.isArray(a.error) && (a.error = [a.error]); let beforeHandle = []; return a.resolve && (beforeHandle = fnToContainer( // @ts-expect-error Array.isArray(a.resolve) ? a.resolve : [a.resolve], "resolve" ), delete a.resolve), a.beforeHandle && (beforeHandle.length ? beforeHandle = beforeHandle.concat( Array.isArray(a.beforeHandle) ? a.beforeHandle : [a.beforeHandle] ) : beforeHandle = Array.isArray(a.beforeHandle) ? a.beforeHandle : [a.beforeHandle]), beforeHandle.length && (a.beforeHandle = beforeHandle), a; }, isBun = typeof Bun < "u", hasBunHash = isBun && typeof Bun.hash == "function", hasSetImmediate = typeof setImmediate == "function", checksum = (s) => { let h = 9; for (let i = 0; i < s.length; ) h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9); return h = h ^ h >>> 9; }, injectChecksum = (checksum2, x) => { if (!x) return; if (!Array.isArray(x)) { const fn = x; return checksum2 && !fn.checksum && (fn.checksum = checksum2), fn.scope === "scoped" && (fn.scope = "local"), fn; } const fns = [...x]; for (const fn of fns) checksum2 && !fn.checksum && (fn.checksum = checksum2), fn.scope === "scoped" && (fn.scope = "local"); return fns; }, mergeLifeCycle = (a, b, checksum2) => ({ start: mergeObjectArray( a.start, injectChecksum(checksum2, b?.start) ), request: mergeObjectArray( a.request, injectChecksum(checksum2, b?.request) ), parse: mergeObjectArray( a.parse, injectChecksum(checksum2, b?.parse) ), transform: mergeObjectArray( a.transform, injectChecksum(checksum2, b?.transform) ), beforeHandle: mergeObjectArray( mergeObjectArray( // @ts-ignore fnToContainer(a.resolve, "resolve"), a.beforeHandle ), injectChecksum( checksum2, mergeObjectArray( fnToContainer(b?.resolve, "resolve"), b?.beforeHandle ) ) ), afterHandle: mergeObjectArray( a.afterHandle, injectChecksum(checksum2, b?.afterHandle) ), mapResponse: mergeObjectArray( a.mapResponse, injectChecksum(checksum2, b?.mapResponse) ), afterResponse: mergeObjectArray( a.afterResponse, injectChecksum(checksum2, b?.afterResponse) ), // Already merged on Elysia._use, also logic is more complicated, can't directly merge trace: mergeObjectArray( a.trace, injectChecksum(checksum2, b?.trace) ), error: mergeObjectArray( a.error, injectChecksum(checksum2, b?.error) ), stop: mergeObjectArray( a.stop, injectChecksum(checksum2, b?.stop) ) }), asHookType = (fn, inject, { skipIfHasType = !1 }) => { if (!fn) return fn; if (!Array.isArray(fn)) return skipIfHasType ? fn.scope ??= inject : fn.scope = inject, fn; for (const x of fn) skipIfHasType ? x.scope ??= inject : x.scope = inject; return fn; }, filterGlobal = (fn) => { if (!fn) return fn; if (!Array.isArray(fn)) switch (fn.scope) { case "global": case "scoped": return { ...fn }; default: return { fn }; } const array = []; for (const x of fn) switch (x.scope) { case "global": case "scoped": array.push({ ...x }); break; } return array; }, filterGlobalHook = (hook) => ({ // rest is validator ...hook, type: hook?.type, detail: hook?.detail, parse: filterGlobal(hook?.parse), transform: filterGlobal(hook?.transform), beforeHandle: filterGlobal(hook?.beforeHandle), afterHandle: filterGlobal(hook?.afterHandle), mapResponse: filterGlobal(hook?.mapResponse), afterResponse: filterGlobal(hook?.afterResponse), error: filterGlobal(hook?.error), trace: filterGlobal(hook?.trace) }), StatusMap = { Continue: 100, "Switching Protocols": 101, Processing: 102, "Early Hints": 103, OK: 200, Created: 201, Accepted: 202, "Non-Authoritative Information": 203, "No Content": 204, "Reset Content": 205, "Partial Content": 206, "Multi-Status": 207, "Already Reported": 208, "Multiple Choices": 300, "Moved Permanently": 301, Found: 302, "See Other": 303, "Not Modified": 304, "Temporary Redirect": 307, "Permanent Redirect": 308, "Bad Request": 400, Unauthorized: 401, "Payment Required": 402, Forbidden: 403, "Not Found": 404, "Method Not Allowed": 405, "Not Acceptable": 406, "Proxy Authentication Required": 407, "Request Timeout": 408, Conflict: 409, Gone: 410, "Length Required": 411, "Precondition Failed": 412, "Payload Too Large": 413, "URI Too Long": 414, "Unsupported Media Type": 415, "Range Not Satisfiable": 416, "Expectation Failed": 417, "I'm a teapot": 418, "Enhance Your Calm": 420, "Misdirected Request": 421, "Unprocessable Content": 422, Locked: 423, "Failed Dependency": 424, "Too Early": 425, "Upgrade Required": 426, "Precondition Required": 428, "Too Many Requests": 429, "Request Header Fields Too Large": 431, "Unavailable For Legal Reasons": 451, "Internal Server Error": 500, "Not Implemented": 501, "Bad Gateway": 502, "Service Unavailable": 503, "Gateway Timeout": 504, "HTTP Version Not Supported": 505, "Variant Also Negotiates": 506, "Insufficient Storage": 507, "Loop Detected": 508, "Not Extended": 510, "Network Authentication Required": 511 }, InvertedStatusMap = Object.fromEntries( Object.entries(StatusMap).map(([k, v]) => [v, k]) ); function removeTrailingEquals(digest) { let trimmedDigest = digest; for (; trimmedDigest.endsWith("="); ) trimmedDigest = trimmedDigest.slice(0, -1); return trimmedDigest; } const encoder = new TextEncoder(), signCookie = async (val, secret) => { if (typeof val == "object" ? val = JSON.stringify(val) : typeof val != "string" && (val = val + ""), secret === null) throw new TypeError("Secret key must be provided."); const secretKey = await crypto.subtle.importKey( "raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, !1, ["sign"] ), hmacBuffer = await crypto.subtle.sign( "HMAC", secretKey, encoder.encode(val) ); return val + "." + removeTrailingEquals(Buffer.from(hmacBuffer).toString("base64")); }, constantTimeEqual = typeof crypto?.timingSafeEqual == "function" ? (a, b) => { const ab = Buffer.from(a, "utf8"), bb = Buffer.from(b, "utf8"); return ab.length !== bb.length ? !1 : crypto.timingSafeEqual(ab, bb); } : (a, b) => a === b, unsignCookie = async (input, secret) => { if (typeof input != "string") throw new TypeError("Signed cookie string must be provided."); if (secret === null) throw new TypeError("Secret key must be provided."); const dot = input.lastIndexOf("."); if (dot <= 0) return !1; const tentativeValue = input.slice(0, dot), expectedInput = await signCookie(tentativeValue, secret); return constantTimeEqual(expectedInput, input) ? tentativeValue : !1; }, insertStandaloneValidator = (hook, name, value) => { if (!hook.standaloneValidator?.length || !Array.isArray(hook.standaloneValidator)) { hook.standaloneValidator = [ { [name]: value } ]; return; } const last = hook.standaloneValidator[hook.standaloneValidator.length - 1]; name in last ? hook.standaloneValidator.push({ [name]: value }) : last[name] = value; }, parseNumericString = (message) => { if (typeof message == "number") return message; if (message.length < 16) { if (message.trim().length === 0) return null; const length = Number(message); return Number.isNaN(length) ? null : length; } if (message.length === 16) { if (message.trim().length === 0) return null; const number = Number(message); return Number.isNaN(number) || number.toString() !== message ? null : number; } return null; }, isNumericString = (message) => parseNumericString(message) !== null; class PromiseGroup { constructor(onError = console.error, onFinally = () => { }) { this.onError = onError; this.onFinally = onFinally; this.root = null; this.promises = []; } /** * The number of promises still being awaited. */ get size() { return this.promises.length; } /** * Add a promise to the group. * @returns The promise that was added. */ add(promise) { return this.promises.push(promise), this.root ||= this.drain(), this.promises.length === 1 && this.then(this.onFinally), promise; } async drain() { for (; this.promises.length > 0; ) { try { await this.promises[0]; } catch (error) { this.onError(error); } this.promises.shift(); } this.root = null; } // Allow the group to be awaited. then(onfulfilled, onrejected) { return (this.root ?? Promise.resolve()).then(onfulfilled, onrejected); } } const fnToContainer = (fn, subType) => { if (!fn) return fn; if (!Array.isArray(fn)) { if (typeof fn == "function" || typeof fn == "string") return subType ? { fn, subType } : { fn }; if ("fn" in fn) return fn; } const fns = []; for (const x of fn) typeof x == "function" || typeof x == "string" ? fns.push(subType ? { fn: x, subType } : { fn: x }) : "fn" in x && fns.push(x); return fns; }, localHookToLifeCycleStore = (a) => (a.start && (a.start = fnToContainer(a.start)), a.request && (a.request = fnToContainer(a.request)), a.parse && (a.parse = fnToContainer(a.parse)), a.transform && (a.transform = fnToContainer(a.transform)), a.beforeHandle && (a.beforeHandle = fnToContainer(a.beforeHandle)), a.afterHandle && (a.afterHandle = fnToContainer(a.afterHandle)), a.mapResponse && (a.mapResponse = fnToContainer(a.mapResponse)), a.afterResponse && (a.afterResponse = fnToContainer(a.afterResponse)), a.trace && (a.trace = fnToContainer(a.trace)), a.error && (a.error = fnToContainer(a.error)), a.stop && (a.stop = fnToContainer(a.stop)), a), lifeCycleToFn = (a) => { const lifecycle = /* @__PURE__ */ Object.create(null); return a.start?.map && (lifecycle.start = a.start.map((x) => x.fn)), a.request?.map && (lifecycle.request = a.request.map((x) => x.fn)), a.parse?.map && (lifecycle.parse = a.parse.map((x) => x.fn)), a.transform?.map && (lifecycle.transform = a.transform.map((x) => x.fn)), a.beforeHandle?.map && (lifecycle.beforeHandle = a.beforeHandle.map((x) => x.fn)), a.afterHandle?.map && (lifecycle.afterHandle = a.afterHandle.map((x) => x.fn)), a.mapResponse?.map && (lifecycle.mapResponse = a.mapResponse.map((x) => x.fn)), a.afterResponse?.map && (lifecycle.afterResponse = a.afterResponse.map((x) => x.fn)), a.error?.map && (lifecycle.error = a.error.map((x) => x.fn)), a.stop?.map && (lifecycle.stop = a.stop.map((x) => x.fn)), a.trace?.map ? lifecycle.trace = a.trace.map((x) => x.fn) : lifecycle.trace = [], lifecycle; }, cloneInference = (inference) => ({ body: inference.body, cookie: inference.cookie, headers: inference.headers, query: inference.query, set: inference.set, server: inference.server, path: inference.path, route: inference.route, url: inference.url }), redirect = (url, status = 302) => Response.redirect(url, status), ELYSIA_FORM_DATA = Symbol("ElysiaFormData"), ELYSIA_REQUEST_ID = Symbol("ElysiaRequestId"), form = (items) => { const formData = new FormData(); if (formData[ELYSIA_FORM_DATA] = {}, items) for (const [key, value] of Object.entries(items)) { if (Array.isArray(value)) { formData[ELYSIA_FORM_DATA][key] = []; for (const v of value) value instanceof File ? formData.append(key, value, value.name) : value instanceof import_file.ElysiaFile ? formData.append(key, value.value, value.value?.name) : formData.append(key, value), formData[ELYSIA_FORM_DATA][key].push(value); continue; } value instanceof File ? formData.append(key, value, value.name) : value instanceof import_file.ElysiaFile ? formData.append(key, value.value, value.value?.name) : formData.append(key, value), formData[ELYSIA_FORM_DATA][key] = value; } return formData; }, randomId = typeof crypto > "u" ? () => { let result = ""; const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", charactersLength = characters.length; for (let i = 0; i < 16; i++) result += characters.charAt( Math.floor(Math.random() * charactersLength) ); return result; } : () => { const uuid = crypto.randomUUID(); return uuid.slice(0, 8) + uuid.slice(24, 32); }, deduplicateChecksum = (array) => { if (!array.length) return []; const hashes = []; for (let i = 0; i < array.length; i++) { const item = array[i]; item.checksum && (hashes.includes(item.checksum) && (array.splice(i, 1), i--), hashes.push(item.checksum)); } return array; }, promoteEvent = (events, as = "scoped") => { if (events) { if (as === "scoped") { for (const event of events) "scope" in event && event.scope === "local" && (event.scope = "scoped"); return; } for (const event of events) "scope" in event && (event.scope = "global"); } }, getLoosePath = (path) => path.charCodeAt(path.length - 1) === 47 ? path.slice(0, path.length - 1) : path + "/", isNotEmpty = (obj) => { if (!obj) return !1; for (const _ in obj) return !0; return !1; }, encodePath = (path, { dynamic = !1 } = {}) => { let encoded = encodeURIComponent(path).replace(/%2F/g, "/"); return dynamic && (encoded = encoded.replace(/%3A/g, ":").replace(/%3F/g, "?")), encoded; }, supportPerMethodInlineHandler = !!(typeof Bun > "u" || Bun.semver?.satisfies?.(Bun.version, ">=1.2.14")), sse = (_payload) => { if (_payload instanceof ReadableStream) return _payload.sse = !0, _payload; const payload = typeof _payload == "string" ? { data: _payload } : _payload; return payload.sse = !0, payload.toSSE = () => { let payloadString = ""; return payload.id !== void 0 && payload.id !== null && (payloadString += `id: ${payload.id} `), payload.event && (payloadString += `event: ${payload.event} `), payload.retry !== void 0 && (payloadString += `retry: ${payload.retry} `), payload.data === null ? payloadString += `data: null ` : typeof payload.data == "string" ? payloadString += `data: ${payload.data} ` : typeof payload.data == "object" && (payloadString += `data: ${JSON.stringify(payload.data)} `), payloadString && (payloadString += ` `), payloadString; }, payload; }; async function getResponseLength(response) { if (response.bodyUsed || !response.body) return 0; let length = 0; const reader = response.body.getReader(); for (; ; ) { const { done, value } = await reader.read(); if (done) break; length += value.byteLength; } return length; } const emptySchema = { headers: !0, cookie: !0, query: !0, params: !0, body: !0, response: !0 }; function deepClone(source, weak = /* @__PURE__ */ new WeakMap()) { if (source === null || typeof source != "object" || typeof source == "function") return source; if (weak.has(source)) return weak.get(source); if (Array.isArray(source)) { const copy = new Array(source.length); weak.set(source, copy); for (let i = 0; i < source.length; i++) copy[i] = deepClone(source[i], weak); return copy; } if (typeof source == "object") { const keys = Object.keys(source).concat( Object.getOwnPropertySymbols(source) ), cloned = {}; weak.set(source, cloned); for (const key of keys) cloned[key] = deepClone(source[key], weak); return cloned; } return source; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { ELYSIA_FORM_DATA, ELYSIA_REQUEST_ID, InvertedStatusMap, PromiseGroup, StatusMap, asHookType, checksum, cloneInference, deduplicateChecksum, deepClone, emptySchema, encodePath, filterGlobalHook, fnToContainer, form, getLoosePath, getResponseLength, hasHeaderShorthand, hasSetImmediate, injectChecksum, insertStandaloneValidator, isClass, isNotEmpty, isNumericString, lifeCycleToArray, lifeCycleToFn, localHookToLifeCycleStore, mergeCookie, mergeDeep, mergeHook, mergeLifeCycle, mergeObjectArray, mergeResponse, mergeSchemaValidator, primitiveHooks, promoteEvent, randomId, redirect, replaceUrlPath, signCookie, sse, supportPerMethodInlineHandler, unsignCookie });