// backend/node_modules/hono/dist/request/constants.js var GET_MATCH_RESULT = /* @__PURE__ */ Symbol(); // backend/node_modules/hono/dist/utils/buffer.js var bufferToFormData = (arrayBuffer, contentType) => { return new Response(arrayBuffer, { headers: { "Content-Type": contentType.replace(/^[^;]+/, (mediaType) => mediaType.toLowerCase()) } }).formData(); }; // backend/node_modules/hono/dist/utils/body.js var MAX_NESTED_OBJECTS = 1e4; var isRawRequest = (request) => "headers" in request; var parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => { const { all = false, dot = false } = options; const mediaType = (isRawRequest(request) ? request.headers : request.raw.headers).get("Content-Type")?.split(";")[0].trim().toLowerCase(); if (mediaType === "multipart/form-data" || mediaType === "application/x-www-form-urlencoded") return parseFormData(request, { all, dot }); return {}; }; async function parseFormData(request, options) { if (!isRawRequest(request) && request.bodyCache.formData) return convertFormDataToBodyData(await request.bodyCache.formData, options); const headers = isRawRequest(request) ? request.headers : request.raw.headers; const arrayBuffer = await request.arrayBuffer(); const formDataPromise = bufferToFormData(arrayBuffer, headers.get("Content-Type") || ""); if (!isRawRequest(request)) request.bodyCache.formData = formDataPromise; const formData = await formDataPromise; if (formData) return convertFormDataToBodyData(formData, options); return {}; } function convertFormDataToBodyData(formData, options) { const form = /* @__PURE__ */ Object.create(null); const nestingState = { count: 0 }; formData.forEach((value, key) => { if (!(options.all || key.endsWith("[]"))) form[key] = value; else handleParsingAllValues(form, key, value); }); if (options.dot) Object.entries(form).forEach(([key, value]) => { if (key.includes(".")) { handleParsingNestedValues(form, key, value, nestingState); delete form[key]; } }); return form; } var handleParsingAllValues = (form, key, value) => { if (form[key] !== void 0) { if (Array.isArray(form[key])) form[key].push(value); else form[key] = [form[key], value]; } else if (!key.endsWith("[]")) form[key] = value; else form[key] = [value]; }; var handleParsingNestedValues = (form, key, value, state) => { if (/(?:^|\.)__proto__\./.test(key)) return; let nestedForm = form; const keys = key.split(".", 34); if (keys.length > 33) throwNestingLimitExceeded(); keys.forEach((key2, index) => { if (index === keys.length - 1) nestedForm[key2] = value; else { if (!nestedForm[key2] || typeof nestedForm[key2] !== "object" || Array.isArray(nestedForm[key2]) || nestedForm[key2] instanceof File) { if (state.count++ >= MAX_NESTED_OBJECTS) throwNestingLimitExceeded(); nestedForm[key2] = /* @__PURE__ */ Object.create(null); } nestedForm = nestedForm[key2]; } }); }; var throwNestingLimitExceeded = () => { throw new Error("Nesting limit exceeded"); }; // backend/node_modules/hono/dist/utils/url.js var splitPath = (path) => { const paths = path.split("/"); if (paths[0] === "") paths.shift(); return paths; }; var splitRoutingPath = (routePath) => { const { groups, path } = extractGroupsFromPath(routePath); const paths = splitPath(path); return replaceGroupMarks(paths, groups); }; var extractGroupsFromPath = (path) => { const groups = []; path = path.replace(/\{[^}]+\}/g, (match2, index) => { const mark = `@${index}`; groups.push([mark, match2]); return mark; }); return { groups, path }; }; var replaceGroupMarks = (paths, groups) => { for (let i = groups.length - 1; i >= 0; i--) { const [mark] = groups[i]; for (let j = paths.length - 1; j >= 0; j--) if (paths[j].includes(mark)) { paths[j] = paths[j].replace(mark, groups[i][1]); break; } } return paths; }; var patternCache = {}; var getPattern = (label, next) => { if (label === "*") return "*"; const match2 = label.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/); if (match2) { const cacheKey = `${label}#${next}`; if (!patternCache[cacheKey]) { if (match2[2]) patternCache[cacheKey] = next && next[0] !== ":" && next[0] !== "*" ? [ cacheKey, match2[1], new RegExp(`^${match2[2]}(?=/${next})`) ] : [ label, match2[1], new RegExp(`^${match2[2]}$`) ]; else patternCache[cacheKey] = [ label, match2[1], true ]; } return patternCache[cacheKey]; } return null; }; var tryDecode = (str, decoder) => { try { return decoder(str); } catch { return str.replace(/(?:%[0-9A-Fa-f]{2})+/g, (match2) => { try { return decoder(match2); } catch { return match2; } }); } }; var tryDecodeURI = (str) => tryDecode(str, decodeURI); var getPath = (request) => { const url = request.url; const start = url.indexOf("/", url.indexOf(":") + 4); let i = start; for (; i < url.length; i++) { const charCode = url.charCodeAt(i); if (charCode === 37) { const queryIndex = url.indexOf("?", i); const hashIndex = url.indexOf("#", i); const end = queryIndex === -1 ? hashIndex === -1 ? void 0 : hashIndex : hashIndex === -1 ? queryIndex : Math.min(queryIndex, hashIndex); const path = url.slice(start, end); return tryDecodeURI(path.includes("%25") ? path.replace(/%25/g, "%2525") : path); } else if (charCode === 63 || charCode === 35) break; } return url.slice(start, i); }; var getPathNoStrict = (request) => { const result = getPath(request); return result.length > 1 && result.at(-1) === "/" ? result.slice(0, -1) : result; }; var mergePath = (base, sub, ...rest) => { if (rest.length) sub = mergePath(sub, ...rest); return `${base?.[0] === "/" ? "" : "/"}${base}${sub === "/" ? "" : `${base?.at(-1) === "/" ? "" : "/"}${sub?.[0] === "/" ? sub.slice(1) : sub}`}`; }; var checkOptionalParameter = (path) => { if (path.charCodeAt(path.length - 1) !== 63 || !path.includes(":")) return null; const segments = path.split("/"); const results = []; let basePath = ""; segments.forEach((segment) => { if (segment !== "" && !/\:/.test(segment)) basePath += "/" + segment; else if (/\:/.test(segment)) { if (segment.charCodeAt(segment.length - 1) === 63) { if (results.length === 0 && basePath === "") results.push("/"); else results.push(basePath); const optionalSegment = segment.slice(0, -1); basePath += "/" + optionalSegment; results.push(basePath); } else basePath += "/" + segment; } }); return results.filter((v, i, a) => a.indexOf(v) === i); }; var tryDecodeURIComponent = (str) => str.indexOf("%") !== -1 ? tryDecode(str, decodeURIComponent_) : str; var _decodeURI = (value) => { if (value.indexOf("+") !== -1) value = value.replace(/\+/g, " "); return tryDecodeURIComponent(value); }; var _getQueryParam = (url, key, multiple) => { const hashIndex = url.indexOf("#", 8); if (hashIndex !== -1) url = url.slice(0, hashIndex); let encoded; if (!multiple && key && key.indexOf("%") === -1 && key.indexOf("+") === -1) { let keyIndex2 = url.indexOf("?", 8); if (keyIndex2 === -1) return; if (!url.startsWith(key, keyIndex2 + 1)) keyIndex2 = url.indexOf(`&${key}`, keyIndex2 + 1); while (keyIndex2 !== -1) { const trailingKeyCode = url.charCodeAt(keyIndex2 + key.length + 1); if (trailingKeyCode === 61) { const valueIndex = keyIndex2 + key.length + 2; const endIndex = url.indexOf("&", valueIndex); return _decodeURI(url.slice(valueIndex, endIndex === -1 ? void 0 : endIndex)); } else if (trailingKeyCode == 38 || isNaN(trailingKeyCode)) return ""; keyIndex2 = url.indexOf(`&${key}`, keyIndex2 + 1); } encoded = /[%+]/.test(url); if (!encoded) return; } const results = /* @__PURE__ */ Object.create(null); encoded ??= /[%+]/.test(url); let keyIndex = url.indexOf("?", 8); while (keyIndex !== -1) { const nextKeyIndex = url.indexOf("&", keyIndex + 1); let valueIndex = url.indexOf("=", keyIndex); if (valueIndex > nextKeyIndex && nextKeyIndex !== -1) valueIndex = -1; let name = url.slice(keyIndex + 1, valueIndex === -1 ? nextKeyIndex === -1 ? void 0 : nextKeyIndex : valueIndex); if (encoded) name = _decodeURI(name); keyIndex = nextKeyIndex; if (name === "") continue; let value; if (valueIndex === -1) value = ""; else { value = url.slice(valueIndex + 1, nextKeyIndex === -1 ? void 0 : nextKeyIndex); if (encoded) value = _decodeURI(value); } if (multiple) { if (!(results[name] && Array.isArray(results[name]))) results[name] = []; results[name].push(value); } else results[name] ??= value; } return key ? results[key] : results; }; var getQueryParam = _getQueryParam; var getQueryParams = (url, key) => { return _getQueryParam(url, key, true); }; var decodeURIComponent_ = decodeURIComponent; // backend/node_modules/hono/dist/request.js var HonoRequest = class { /** * `.raw` can get the raw Request object. * * @see {@link https://hono.dev/docs/api/request#raw} * * @example * ```ts * // For Cloudflare Workers * app.post('/', async (c) => { * const metadata = c.req.raw.cf?.hostMetadata? * ... * }) * ``` */ raw; #validatedData; #matchResult; routeIndex = 0; /** * `.path` can get the pathname of the request. * * @see {@link https://hono.dev/docs/api/request#path} * * @example * ```ts * app.get('/about/me', (c) => { * const pathname = c.req.path // `/about/me` * }) * ``` */ path; bodyCache = {}; constructor(request, path = "/", matchResult = [[]]) { this.raw = request; this.path = path; this.#matchResult = matchResult; } param(key) { return key ? this.#getDecodedParam(key) : this.#getAllDecodedParams(); } #getDecodedParam(key) { const paramKey = this.#matchResult[0][this.routeIndex]?.[1][key]; const param = this.#getParamValue(paramKey); return param && tryDecodeURIComponent(param); } #getAllDecodedParams() { const decoded = {}; const keys = Object.keys(this.#matchResult[0][this.routeIndex]?.[1] ?? {}); for (const key of keys) { const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]); if (value !== void 0) decoded[key] = tryDecodeURIComponent(value); } return decoded; } #getParamValue(paramKey) { return this.#matchResult[1] ? this.#matchResult[1][paramKey] : paramKey; } query(key) { return getQueryParam(this.url, key); } queries(key) { return getQueryParams(this.url, key); } header(name) { if (name) return this.raw.headers.get(name) ?? void 0; const headerData = /* @__PURE__ */ Object.create(null); this.raw.headers.forEach((value, key) => { headerData[key] = value; }); return headerData; } async parseBody(options) { return parseBody(this, options); } #cachedBody = (key) => { const { bodyCache, raw: raw2 } = this; const cachedBody = bodyCache[key]; if (cachedBody) return cachedBody; for (const anyCachedKey in bodyCache) return bodyCache[anyCachedKey].then((body) => { if (anyCachedKey === "json") body = JSON.stringify(body); const contentType = anyCachedKey === "formData" ? void 0 : raw2.headers.get("content-type"); return new Response(body, { headers: contentType ? { "Content-Type": contentType } : void 0 })[key](); }); return bodyCache[key] = raw2[key](); }; /** * `.json()` can parse Request body of type `application/json` * * @see {@link https://hono.dev/docs/api/request#json} * * @example * ```ts * app.post('/entry', async (c) => { * const body = await c.req.json() * }) * ``` */ json() { return this.#cachedBody("text").then((text) => JSON.parse(text)); } /** * `.text()` can parse Request body of type `text/plain` * * @see {@link https://hono.dev/docs/api/request#text} * * @example * ```ts * app.post('/entry', async (c) => { * const body = await c.req.text() * }) * ``` */ text() { return this.#cachedBody("text"); } /** * `.arrayBuffer()` parse Request body as an `ArrayBuffer` * * @see {@link https://hono.dev/docs/api/request#arraybuffer} * * @example * ```ts * app.post('/entry', async (c) => { * const body = await c.req.arrayBuffer() * }) * ``` */ arrayBuffer() { return this.#cachedBody("arrayBuffer"); } /** * `.bytes()` parses the request body as a `Uint8Array`. * * @see {@link https://hono.dev/docs/api/request#bytes} * * @example * ```ts * app.post('/entry', async (c) => { * const body = await c.req.bytes() * }) * ``` */ bytes() { return this.#cachedBody("arrayBuffer").then((buffer) => new Uint8Array(buffer)); } /** * Parses the request body as a `Blob`. * @example * ```ts * app.post('/entry', async (c) => { * const body = await c.req.blob(); * }); * ``` * @see https://hono.dev/docs/api/request#blob */ blob() { return this.#cachedBody("blob"); } /** * Parses the request body as `FormData`. * @example * ```ts * app.post('/entry', async (c) => { * const body = await c.req.formData(); * }); * ``` * @see https://hono.dev/docs/api/request#formdata */ formData() { return this.#cachedBody("formData"); } /** * Adds validated data to the request. * * @param target - The target of the validation. * @param data - The validated data to add. */ addValidatedData(target, data) { (this.#validatedData ??= {})[target] = data; } valid(target) { return this.#validatedData?.[target]; } /** * `.url()` can get the request url strings. * * @see {@link https://hono.dev/docs/api/request#url} * * @example * ```ts * app.get('/about/me', (c) => { * const url = c.req.url // `http://localhost:8787/about/me` * ... * }) * ``` */ get url() { return this.raw.url; } /** * `.method()` can get the method name of the request. * * @see {@link https://hono.dev/docs/api/request#method} * * @example * ```ts * app.get('/about/me', (c) => { * const method = c.req.method // `GET` * }) * ``` */ get method() { return this.raw.method; } get [GET_MATCH_RESULT]() { return this.#matchResult; } /** * `.matchedRoutes()` can return a matched route in the handler * * @deprecated * * Use matchedRoutes helper defined in "hono/route" instead. * * @see {@link https://hono.dev/docs/api/request#matchedroutes} * * @example * ```ts * app.use('*', async function logger(c, next) { * await next() * c.req.matchedRoutes.forEach(({ handler, method, path }, i) => { * const name = handler.name || (handler.length < 2 ? '[handler]' : '[middleware]') * console.log( * method, * ' ', * path, * ' '.repeat(Math.max(10 - path.length, 0)), * name, * i === c.req.routeIndex ? '<- respond from here' : '' * ) * }) * }) * ``` */ get matchedRoutes() { return this.#matchResult[0].map(([[, route]]) => route); } /** * `routePath()` can retrieve the path registered within the handler * * @deprecated * * Use routePath helper defined in "hono/route" instead. * * @see {@link https://hono.dev/docs/api/request#routepath} * * @example * ```ts * app.get('/posts/:id', (c) => { * return c.json({ path: c.req.routePath }) * }) * ``` */ get routePath() { return this.#matchResult[0].map(([[, route]]) => route)[this.routeIndex].path; } }; // backend/node_modules/hono/dist/utils/html.js var HtmlEscapedCallbackPhase = { Stringify: 1, BeforeStream: 2, Stream: 3 }; var raw = (value, callbacks) => { const escapedString = new String(value); escapedString.isEscaped = true; escapedString.callbacks = callbacks; return escapedString; }; var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => { if (typeof str === "object" && !(str instanceof String)) { if (!(str instanceof Promise)) str = str.toString(); if (str instanceof Promise) str = await str; } const callbacks = str.callbacks; if (!callbacks?.length) return Promise.resolve(str); if (buffer) buffer[0] += str; else buffer = [str]; const resStr = Promise.all(callbacks.map((c) => c({ phase, buffer, context }))).then((res) => Promise.all(res.filter(Boolean).map((str2) => resolveCallback(str2, phase, false, context, buffer))).then(() => buffer[0])); if (preserveCallbacks) return raw(await resStr, callbacks); else return resStr; }; // backend/node_modules/hono/dist/context.js var TEXT_PLAIN = "text/plain; charset=UTF-8"; var setDefaultContentType = (contentType, headers) => { return { "Content-Type": contentType, ...headers }; }; var createResponseInstance = (body, init) => new Response(body, init); var Context = class { #rawRequest; #req; /** * `.env` can get bindings (environment variables, secrets, KV namespaces, D1 database, R2 bucket etc.) in Cloudflare Workers. * * @see {@link https://hono.dev/docs/api/context#env} * * @example * ```ts * // Environment object for Cloudflare Workers * app.get('*', async c => { * const counter = c.env.COUNTER * }) * ``` */ env = {}; #var; finalized = false; /** * `.error` can get the error object from the middleware if the Handler throws an error. * * @see {@link https://hono.dev/docs/api/context#error} * * @example * ```ts * app.use('*', async (c, next) => { * await next() * if (c.error) { * // do something... * } * }) * ``` */ error; #status; #executionCtx; #res; #layout; #renderer; #notFoundHandler; #preparedHeaders; #matchResult; #path; /** * Creates an instance of the Context class. * * @param req - The Request object. * @param options - Optional configuration options for the context. */ constructor(req, options) { this.#rawRequest = req; if (options) { this.#executionCtx = options.executionCtx; this.env = options.env; this.#notFoundHandler = options.notFoundHandler; this.#path = options.path; this.#matchResult = options.matchResult; } } /** * `.req` is the instance of {@link HonoRequest}. */ get req() { this.#req ??= new HonoRequest(this.#rawRequest, this.#path, this.#matchResult); return this.#req; } /** * @see {@link https://hono.dev/docs/api/context#event} * The FetchEvent associated with the current request. * * @throws Will throw an error if the context does not have a FetchEvent. */ get event() { if (this.#executionCtx && "respondWith" in this.#executionCtx) return this.#executionCtx; else throw Error("This context has no FetchEvent"); } /** * @see {@link https://hono.dev/docs/api/context#executionctx} * The ExecutionContext associated with the current request. * * @throws Will throw an error if the context does not have an ExecutionContext. */ get executionCtx() { if (this.#executionCtx) return this.#executionCtx; else throw Error("This context has no ExecutionContext"); } /** * @see {@link https://hono.dev/docs/api/context#res} * The Response object for the current request. */ get res() { return this.#res ||= createResponseInstance(null, { headers: this.#preparedHeaders ??= new Headers() }); } /** * Sets the Response object for the current request. * * @param _res - The Response object to set. */ set res(_res) { if (this.#res && _res) { _res = createResponseInstance(_res.body, _res); for (const [k, v] of this.#res.headers.entries()) { if (k === "content-type") continue; if (k === "set-cookie") { const cookies = this.#res.headers.getSetCookie(); _res.headers.delete("set-cookie"); for (const cookie of cookies) _res.headers.append("set-cookie", cookie); } else _res.headers.set(k, v); } } this.#res = _res; this.finalized = true; } /** * `.render()` can create a response within a layout. * * @see {@link https://hono.dev/docs/api/context#render-setrenderer} * * @example * ```ts * app.get('/', (c) => { * return c.render('Hello!') * }) * ``` */ render = (...args) => { this.#renderer ??= (content) => this.html(content); return this.#renderer(...args); }; /** * Sets the layout for the response. * * @param layout - The layout to set. * @returns The layout function. */ setLayout = (layout) => this.#layout = layout; /** * Gets the current layout for the response. * * @returns The current layout function. */ getLayout = () => this.#layout; /** * `.setRenderer()` can set the layout in the custom middleware. * * @see {@link https://hono.dev/docs/api/context#render-setrenderer} * * @example * ```tsx * app.use('*', async (c, next) => { * c.setRenderer((content) => { * return c.html( * * *

{content}

* * * ) * }) * await next() * }) * ``` */ setRenderer = (renderer) => { this.#renderer = renderer; }; /** * `.header()` can set headers. * * @see {@link https://hono.dev/docs/api/context#header} * * @example * ```ts * app.get('/welcome', (c) => { * // Set headers * c.header('X-Message', 'Hello!') * c.header('Content-Type', 'text/plain') * * // Append multiple headers using the append option (e.g. Vary) * c.header('Vary', 'Accept-Encoding', { append: true }) * c.header('Vary', 'User-Agent', { append: true }) * * return c.body('Thank you for coming') * }) * ``` */ header = (name, value, options) => { if (this.finalized) this.#res = createResponseInstance(this.#res.body, this.#res); const headers = this.#res ? this.#res.headers : this.#preparedHeaders ??= new Headers(); if (value === void 0) headers.delete(name); else if (options?.append) headers.append(name, value); else headers.set(name, value); }; status = (status) => { this.#status = status; }; /** * `.set()` can set the value specified by the key. * * @see {@link https://hono.dev/docs/api/context#set-get} * * @example * ```ts * app.use('*', async (c, next) => { * c.set('message', 'Hono is hot!!') * await next() * }) * ``` */ set = (key, value) => { this.#var ??= /* @__PURE__ */ new Map(); this.#var.set(key, value); }; /** * `.get()` can use the value specified by the key. * * @see {@link https://hono.dev/docs/api/context#set-get} * * @example * ```ts * app.get('/', (c) => { * const message = c.get('message') * return c.text(`The message is "${message}"`) * }) * ``` */ get = (key) => { return this.#var ? this.#var.get(key) : void 0; }; /** * `.var` can access the value of a variable. * * @see {@link https://hono.dev/docs/api/context#var} * * @example * ```ts * const result = c.var.client.oneMethod() * ``` */ get var() { if (!this.#var) return {}; return Object.fromEntries(this.#var); } #newResponse(data, arg, headers) { let responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders; if (typeof arg === "object" && arg.headers) { responseHeaders ??= new Headers(); for (const [key, value] of new Headers(arg.headers)) if (key === "set-cookie") responseHeaders.append(key, value); else responseHeaders.set(key, value); } if (headers) { if (!responseHeaders) { let count = 0; for (const k in headers) if (++count > 1 || typeof headers[k] !== "string") { responseHeaders = new Headers(); break; } } if (responseHeaders) for (const k in headers) { const v = headers[k]; if (typeof v === "string") responseHeaders.set(k, v); else { responseHeaders.delete(k); for (const v2 of v) responseHeaders.append(k, v2); } } } const status = typeof arg === "number" ? arg : arg?.status ?? this.#status; return createResponseInstance(data, { status, headers: responseHeaders ?? headers }); } newResponse = (...args) => this.#newResponse(...args); /** * `.body()` can return the HTTP response. * You can set headers with `.header()` and set HTTP status code with `.status`. * This can also be set in `.text()`, `.json()` and so on. * * @see {@link https://hono.dev/docs/api/context#body} * * @example * ```ts * app.get('/welcome', (c) => { * // Set headers * c.header('X-Message', 'Hello!') * c.header('Content-Type', 'text/plain') * // Set HTTP status code * c.status(201) * * // Return the response body * return c.body('Thank you for coming') * }) * ``` */ body = (data, arg, headers) => this.#newResponse(data, arg, headers); /** * `.text()` can render text as `Content-Type:text/plain`. * * @see {@link https://hono.dev/docs/api/context#text} * * @example * ```ts * app.get('/say', (c) => { * return c.text('Hello!') * }) * ``` */ text = (text, arg, headers) => { return !this.#preparedHeaders && !this.#status && !arg && !headers && !this.finalized ? new Response(text) : this.#newResponse(text, arg, setDefaultContentType(TEXT_PLAIN, headers)); }; /** * `.json()` can render JSON as `Content-Type:application/json`. * * @see {@link https://hono.dev/docs/api/context#json} * * @example * ```ts * app.get('/api', (c) => { * return c.json({ message: 'Hello!' }) * }) * ``` */ json = (object, arg, headers) => { return this.#newResponse(JSON.stringify(object), arg, setDefaultContentType("application/json", headers)); }; html = (html, arg, headers) => { const res = (html2) => this.#newResponse(html2, arg, setDefaultContentType("text/html; charset=UTF-8", headers)); return typeof html === "object" ? resolveCallback(html, HtmlEscapedCallbackPhase.Stringify, false, {}).then(res) : res(html); }; /** * `.redirect()` can Redirect, default status code is 302. * * @see {@link https://hono.dev/docs/api/context#redirect} * * @example * ```ts * app.get('/redirect', (c) => { * return c.redirect('/') * }) * app.get('/redirect-permanently', (c) => { * return c.redirect('/', 301) * }) * ``` */ redirect = (location, status) => { const locationString = String(location); this.header("Location", !/[^\x00-\xFF]/.test(locationString) ? locationString : encodeURI(locationString)); return this.newResponse(null, status ?? 302); }; /** * `.notFound()` can return the Not Found Response. * * @see {@link https://hono.dev/docs/api/context#notfound} * * @example * ```ts * app.get('/notfound', (c) => { * return c.notFound() * }) * ``` */ notFound = () => { this.#notFoundHandler ??= () => createResponseInstance(); return this.#notFoundHandler(this); }; }; // backend/node_modules/hono/dist/compose.js var compose = (middleware, onError, onNotFound) => { return (context, next) => { let index = -1; return dispatch(0); async function dispatch(i) { if (i <= index) throw new Error("next() called multiple times"); index = i; let res; let isError = false; let handler; if (middleware[i]) { handler = middleware[i][0][0]; context.req.routeIndex = i; } else handler = i === middleware.length && next || void 0; if (handler) try { res = await handler(context, () => dispatch(i + 1)); } catch (err) { if (err instanceof Error && onError) { context.error = err; res = await onError(err, context); isError = true; } else throw err; } else if (context.finalized === false && onNotFound) res = await onNotFound(context); if (res && (context.finalized === false || isError)) context.res = res; return context; } }; }; // backend/node_modules/hono/dist/router.js var METHODS = [ "get", "post", "put", "delete", "options", "patch", "query" ]; var MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is already built."; var UnsupportedPathError = class extends Error { }; // backend/node_modules/hono/dist/utils/constants.js var COMPOSED_HANDLER = "__COMPOSED_HANDLER"; // backend/node_modules/hono/dist/hono-base.js var notFoundHandler = (c) => { return c.text("404 Not Found", 404); }; var errorHandler = (err, c) => { if ("getResponse" in err) { const res = err.getResponse(); return c.newResponse(res.body, res); } console.error(err); return c.text("Internal Server Error", 500); }; var Hono = class Hono2 { get; post; put; delete; options; patch; query; all; on; use; router; getPath; _basePath = "/"; #path = "/"; routes = []; constructor(options = {}) { [...METHODS, "all"].forEach((method) => { this[method] = (args1, ...args) => { const methodName = method.toUpperCase(); if (typeof args1 === "string") this.#path = args1; else this.#addRoute(methodName, this.#path, args1); args.forEach((handler) => { this.#addRoute(methodName, this.#path, handler); }); return this; }; }); this.on = (method, path, ...handlers) => { for (const p of [path].flat()) { this.#path = p; for (const m of [method].flat()) { const methodName = m.toUpperCase(); for (const handler of handlers) this.#addRoute(methodName, this.#path, handler); } } return this; }; this.use = (arg1, ...handlers) => { if (typeof arg1 === "string") this.#path = arg1; else { this.#path = "*"; handlers.unshift(arg1); } handlers.forEach((handler) => { this.#addRoute("ALL", this.#path, handler); }); return this; }; const { strict, ...optionsWithoutStrict } = options; Object.assign(this, optionsWithoutStrict); this.getPath = strict ?? true ? options.getPath ?? getPath : getPathNoStrict; } #clone() { const clone = new Hono2({ router: this.router, getPath: this.getPath }); clone.errorHandler = this.errorHandler; clone.#notFoundHandler = this.#notFoundHandler; clone.routes = this.routes; return clone; } #notFoundHandler = notFoundHandler; errorHandler = errorHandler; /** * `.route()` allows grouping other Hono instance in routes. * * @see {@link https://hono.dev/docs/api/routing#grouping} * * @param {string} path - base Path * @param {Hono} app - other Hono instance * @returns {Hono} routed Hono instance * * @example * ```ts * const app = new Hono() * const app2 = new Hono() * * app2.get("/user", (c) => c.text("user")) * app.route("/api", app2) // GET /api/user * ``` */ route(path, app2) { const subApp = this.basePath(path); app2.routes.map((r) => { let handler; if (app2.errorHandler === errorHandler) handler = r.handler; else { handler = async (c, next) => (await compose([], app2.errorHandler)(c, () => r.handler(c, next))).res; handler[COMPOSED_HANDLER] = r.handler; } subApp.#addRoute(r.method, r.path, handler, r.basePath); }); return this; } /** * `.basePath()` allows base paths to be specified. * * @see {@link https://hono.dev/docs/api/routing#base-path} * * @param {string} path - base Path * @returns {Hono} changed Hono instance * * @example * ```ts * const api = new Hono().basePath('/api') * ``` */ basePath(path) { const subApp = this.#clone(); subApp._basePath = mergePath(this._basePath, path); return subApp; } /** * `.onError()` handles an error and returns a customized Response. * * @see {@link https://hono.dev/docs/api/hono#error-handling} * * @param {ErrorHandler} handler - request Handler for error * @returns {Hono} changed Hono instance * * @example * ```ts * app.onError((err, c) => { * console.error(`${err}`) * return c.text('Custom Error Message', 500) * }) * ``` */ onError = (handler) => { this.errorHandler = handler; return this; }; /** * `.notFound()` allows you to customize a Not Found Response. * * @see {@link https://hono.dev/docs/api/hono#not-found} * * @param {NotFoundHandler} handler - request handler for not-found * @returns {Hono} changed Hono instance * * @example * ```ts * app.notFound((c) => { * return c.text('Custom 404 Message', 404) * }) * ``` */ notFound = (handler) => { this.#notFoundHandler = handler; return this; }; /** * `.mount()` allows you to mount applications built with other frameworks into your Hono application. * * @see {@link https://hono.dev/docs/api/hono#mount} * * @param {string} path - base Path * @param {Function} applicationHandler - other Request Handler * @param {MountOptions} [options] - options of `.mount()` * @returns {Hono} mounted Hono instance * * @example * ```ts * import { Router as IttyRouter } from 'itty-router' * import { Hono } from 'hono' * // Create itty-router application * const ittyRouter = IttyRouter() * // GET /itty-router/hello * ittyRouter.get('/hello', () => new Response('Hello from itty-router')) * * const app = new Hono() * app.mount('/itty-router', ittyRouter.handle) * ``` * * @example * ```ts * const app = new Hono() * // Send the request to another application without modification. * app.mount('/app', anotherApp, { * replaceRequest: (req) => req, * }) * ``` */ mount(path, applicationHandler, options) { let replaceRequest; let optionHandler; if (options) { if (typeof options === "function") optionHandler = options; else { optionHandler = options.optionHandler; if (options.replaceRequest === false) replaceRequest = (request) => request; else replaceRequest = options.replaceRequest; } } const getOptions = optionHandler ? (c) => { const options2 = optionHandler(c); return Array.isArray(options2) ? options2 : [options2]; } : (c) => { let executionContext = void 0; try { executionContext = c.executionCtx; } catch { } return [c.env, executionContext]; }; replaceRequest ||= (() => { const mergedPath = mergePath(this._basePath, path); const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length; return (request) => { const url = new URL(request.url); url.pathname = this.getPath(request).slice(pathPrefixLength) || "/"; return new Request(url, request); }; })(); const handler = async (c, next) => { const res = await applicationHandler(replaceRequest(c.req.raw), ...getOptions(c)); if (res) return res; await next(); }; this.#addRoute("ALL", mergePath(path, "*"), handler); return this; } #addRoute(method, path, handler, baseRoutePath) { path = mergePath(this._basePath, path); const r = { basePath: baseRoutePath !== void 0 ? mergePath(this._basePath, baseRoutePath) : this._basePath, path, method, handler }; this.router.add(method, path, [handler, r]); this.routes.push(r); } #handleError(err, c) { if (err instanceof Error) return this.errorHandler(err, c); throw err; } #dispatch(request, executionCtx, env, method) { if (method === "HEAD") return (async () => new Response(null, await this.#dispatch(request, executionCtx, env, "GET")))(); const path = this.getPath(request, { env }); const matchResult = this.router.match(method, path); const c = new Context(request, { path, matchResult, env, executionCtx, notFoundHandler: this.#notFoundHandler }); if (matchResult[0].length === 1) { let res; try { res = matchResult[0][0][0][0](c, async () => { c.res = await this.#notFoundHandler(c); }); } catch (err) { return this.#handleError(err, c); } return res instanceof Promise ? res.then((resolved) => resolved || (c.finalized ? c.res : this.#notFoundHandler(c))).catch((err) => this.#handleError(err, c)) : res ?? this.#notFoundHandler(c); } const composed = compose(matchResult[0], this.errorHandler, this.#notFoundHandler); return (async () => { try { const context = await composed(c); if (!context.finalized) throw new Error("Context is not finalized. Did you forget to return a Response object or `await next()`?"); return context.res; } catch (err) { return this.#handleError(err, c); } })(); } /** * `.fetch()` will be entry point of your app. * * @see {@link https://hono.dev/docs/api/hono#fetch} * * @param {Request} request - request Object of request * @param {Env} env - env Object * @param {ExecutionContext} executionCtx - context of execution * @returns {Response | Promise} response of request * */ fetch = (request, ...rest) => { return this.#dispatch(request, rest[1], rest[0], request.method); }; /** * `.request()` is a useful method for testing. * You can pass a URL or pathname to send a GET request. * app will return a Response object. * ```ts * test('GET /hello is ok', async () => { * const res = await app.request('/hello') * expect(res.status).toBe(200) * }) * ``` * @see https://hono.dev/docs/api/hono#request */ request = (input, requestInit, Env, executionCtx) => { if (input instanceof Request) return this.fetch(requestInit ? new Request(input, requestInit) : input, Env, executionCtx); input = input.toString(); return this.fetch(new Request(/^https?:\/\//.test(input) ? input : `http://localhost${mergePath("/", input)}`, requestInit), Env, executionCtx); }; /** * `.fire()` automatically adds a global fetch event listener. * This can be useful for environments that adhere to the Service Worker API, such as non-ES module Cloudflare Workers. * @deprecated * Use `fire` from `hono/service-worker` instead. * ```ts * import { Hono } from 'hono' * import { fire } from 'hono/service-worker' * * const app = new Hono() * // ... * fire(app) * ``` * @see https://hono.dev/docs/api/hono#fire * @see https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API * @see https://developers.cloudflare.com/workers/reference/migrate-to-module-workers/ */ fire = () => { addEventListener("fetch", (event) => { event.respondWith(this.#dispatch(event.request, event, void 0, event.request.method)); }); }; }; // backend/node_modules/hono/dist/router/utils.js var createNullObject = () => /* @__PURE__ */ Object.create(null); // backend/node_modules/hono/dist/router/reg-exp-router/matcher.js var emptyParam = []; function match(method, path) { const matchers = this.buildAllMatchers(); const match2 = ((method2, path2) => { const matcher = matchers[method2] || matchers["ALL"]; const staticMatch = matcher[2][path2]; if (staticMatch) return staticMatch; const match3 = path2.match(matcher[0]); if (!match3) return [[], emptyParam]; const index = match3.indexOf("", 1); return [matcher[1][index], match3]; }); this.match = match2; return match2(method, path); } // backend/node_modules/hono/dist/router/reg-exp-router/node.js var LABEL_REG_EXP_STR = "[^/]+"; var TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)"; var PATH_ERROR = /* @__PURE__ */ Symbol(); var regExpMetaChars = /* @__PURE__ */ new Set(".\\+*[^]$()"); function compareKey(a, b) { if (a.length === 1) return b.length === 1 ? a < b ? -1 : 1 : -1; if (b.length === 1) return 1; if (a === ".*" || a === "(?:|/.*)") return b === "(?:|/.*)" ? -1 : 1; else if (b === ".*" || b === "(?:|/.*)") return -1; if (a === "[^/]+") return 1; else if (b === "[^/]+") return -1; return a.length === b.length ? a < b ? -1 : 1 : b.length - a.length; } var Node = class Node2 { #index; #varIndex; #children = createNullObject(); insert(tokens, index, paramMap, context, isStatic) { let node = this; for (let i = 0, len = tokens.length; i < len; i++) { const token = tokens[i]; const pattern = token.length === 1 ? token === "*" ? i === len - 1 ? [ "", "", ".*" ] : [ "", "", LABEL_REG_EXP_STR ] : null : token === "/*" ? [ "", "", TAIL_WILDCARD_REG_EXP_STR ] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/); let nextNode; if (pattern) { const name = pattern[1]; let regexpStr = pattern[2] || "[^/]+"; if (name && pattern[2]) { if (regexpStr === ".*") throw PATH_ERROR; regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:"); if (/\((?!\?:)/.test(regexpStr)) throw PATH_ERROR; if (regexpStr.length === 1 && regExpMetaChars.has(regexpStr)) throw PATH_ERROR; } nextNode = node.#children[regexpStr]; if (!nextNode) { if (regexpStr !== ".*" && regexpStr !== "(?:|/.*)") { for (const k in node.#children) if ((regexpStr.length > 1 || k.length > 1) && k !== ".*" && k !== "(?:|/.*)") throw PATH_ERROR; } nextNode = node.#children[regexpStr] = new Node2(); } if (name !== "") { nextNode.#varIndex ??= context.varIndex++; paramMap.push([name, nextNode.#varIndex]); } } else { nextNode = node.#children[token]; if (!nextNode) { for (const k in node.#children) if (k.length > 1 && k !== ".*" && k !== "(?:|/.*)") throw PATH_ERROR; nextNode = node.#children[token] = new Node2(); } } node = nextNode; } if (node.#index !== void 0) throw PATH_ERROR; node.#index = isStatic ? -1 : index; } buildRegExpStr() { const strList = Object.keys(this.#children).sort(compareKey).map((k) => { const c = this.#children[k]; const childStr = c.buildRegExpStr(); return childStr === "" ? "" : (typeof c.#varIndex === "number" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + childStr; }).filter(Boolean); if (typeof this.#index === "number" && this.#index !== -1) strList.unshift(`#${this.#index}`); if (strList.length === 0) return ""; if (strList.length === 1) return strList[0]; return "(?:" + strList.join("|") + ")"; } }; // backend/node_modules/hono/dist/router/reg-exp-router/trie.js var Trie = class { #context = { varIndex: 0 }; #root = new Node(); #index = 0; paths = createNullObject(); insert(path, isStatic) { if (isStatic) { this.#root.insert(path.split(""), 0, [], this.#context, true); return; } const paramAssoc = []; const groups = []; let markedPath = path; for (let i = 0; ; ) { let replaced = false; markedPath = markedPath.replace(/\{[^}]+\}/g, (m) => { const mark = `@\\${i}`; groups[i] = [mark, m]; i++; replaced = true; return mark; }); if (!replaced) break; } const tokens = markedPath.match(/(?::[^\/]+)|(?:\/\*$)|./g) || []; for (let i = groups.length - 1; i >= 0; i--) { const [mark] = groups[i]; for (let j = tokens.length - 1; j >= 0; j--) if (tokens[j].indexOf(mark) !== -1) { tokens[j] = tokens[j].replace(mark, groups[i][1]); break; } } this.#root.insert(tokens, this.#index, paramAssoc, this.#context, false); this.paths[path] = [this.#index++, paramAssoc]; } buildRegExp() { let regexp = this.#root.buildRegExpStr(); if (regexp === "") return [ /^$/, [], [] ]; let captureIndex = 0; const indexReplacementMap = []; const paramReplacementMap = []; regexp = regexp.replace(/#(\d+)|@(\d+)|\.\*\$/g, (_, handlerIndex, paramIndex) => { if (handlerIndex !== void 0) { indexReplacementMap[++captureIndex] = Number(handlerIndex); return "$()"; } if (paramIndex !== void 0) { paramReplacementMap[Number(paramIndex)] = ++captureIndex; return ""; } return ""; }); return [ new RegExp(`^${regexp}`), indexReplacementMap, paramReplacementMap ]; } }; // backend/node_modules/hono/dist/router/reg-exp-router/router.js var wildcardRegExpCache = createNullObject(); function buildWildcardRegExp(path) { return wildcardRegExpCache[path] ??= new RegExp(`^${path.replace(/\/:[^/{}]+(?:\{\[\^\/]\+})?(?=[/{]|$)|\/?\*$|([.\\+*[^\]$()?{}|])/g, (match2, metaChar) => metaChar ? `\\${metaChar}` : match2 === "/*" ? TAIL_WILDCARD_REG_EXP_STR : match2 === "*" ? ".*" : `/:${LABEL_REG_EXP_STR}`)}$`); } function findMiddleware(middleware, path) { for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) if (buildWildcardRegExp(k).test(path)) return [...middleware[k]]; } var RegExpRouter = class { name = "RegExpRouter"; #middleware; #routes; #tries; constructor() { this.#middleware = { ["ALL"]: createNullObject() }; this.#routes = { ["ALL"]: createNullObject() }; this.#tries = { ["ALL"]: new Trie() }; } #insertPath(method, path) { try { this.#tries[method].insert(path, !/\*|\/:/.test(path)); } catch (e) { throw e === PATH_ERROR ? new UnsupportedPathError(path) : e; } } add(method, path, handler) { const middleware = this.#middleware; const routes = this.#routes; if (!middleware) throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT); if (!middleware[method]) { this.#tries[method] = new Trie(); for (const handlerMap of [middleware, routes]) { handlerMap[method] = createNullObject(); for (const p in handlerMap["ALL"]) { handlerMap[method][p] = [...handlerMap["ALL"][p]]; this.#insertPath(method, p); } } } if (path === "/*") path = "*"; const methods = method === "ALL" ? Object.keys(middleware) : [method]; if (/\*$/.test(path)) { const re = buildWildcardRegExp(path); for (const m of methods) if (!middleware[m][path]) { this.#insertPath(m, path); middleware[m][path] = findMiddleware(middleware[m], path) || findMiddleware(middleware["ALL"], path) || []; } for (const handlerMap of [middleware, routes]) for (const m of methods) for (const p in handlerMap[m]) re.test(p) && handlerMap[m][p].push([handler, path]); return; } const paths = checkOptionalParameter(path) || [path]; for (const path2 of paths) for (const m of methods) { if (!routes[m][path2]) { this.#insertPath(m, path2); routes[m][path2] = findMiddleware(middleware[m], path2) || findMiddleware(middleware["ALL"], path2) || []; } routes[m][path2].push([handler, path2]); } } match = match; buildAllMatchers() { const matchers = createNullObject(); for (const method of Object.keys(this.#routes)) matchers[method] = this.#buildMatcher(method); this.#middleware = this.#routes = this.#tries = void 0; wildcardRegExpCache = createNullObject(); return matchers; } #buildMatcher(method) { const middleware = this.#middleware[method]; const routes = this.#routes[method]; const trie = this.#tries[method]; const staticMap = createNullObject(); const handlerData = []; const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp(); for (const r of [middleware, routes]) for (const path in r) { const handlers = r[path]; const pathData = trie.paths[path]; if (!pathData) { staticMap[path] = [handlers.map(([h]) => [h, createNullObject()]), emptyParam]; continue; } handlerData[pathData[0]] = handlers.map(([h, handlerPath]) => [h, trie.paths[handlerPath][1].reduceRight((map, [key], i) => { map[key] = paramReplacementMap[pathData[1][i][1]]; return map; }, createNullObject())]); } return [ regexp, indexReplacementMap.map((i) => handlerData[i]), staticMap ]; } }; // backend/node_modules/hono/dist/router/smart-router/router.js var SmartRouter = class { name = "SmartRouter"; #routers = []; #routes = []; constructor(init) { this.#routers = init.routers; } add(method, path, handler) { if (!this.#routes) throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT); this.#routes.push([ method, path, handler ]); } match(method, path) { if (!this.#routes) throw new Error("Fatal error"); const routers = this.#routers; const routes = this.#routes; const len = routers.length; let i = 0; let res; for (; i < len; i++) { const router = routers[i]; try { for (let i2 = 0, len2 = routes.length; i2 < len2; i2++) router.add(...routes[i2]); res = router.match(method, path); } catch (e) { if (e instanceof UnsupportedPathError) continue; throw e; } this.match = router.match.bind(router); this.#routers = [router]; this.#routes = void 0; break; } if (i === len) throw new Error("Fatal error"); this.name = `SmartRouter + ${this.activeRouter.name}`; return res; } get activeRouter() { if (this.#routes || this.#routers.length !== 1) throw new Error("No active router has been determined yet."); return this.#routers[0]; } }; // backend/node_modules/hono/dist/router/trie-router/node.js var emptyParams = createNullObject(); var order = 0; var Node3 = class Node4 { #methods = []; #children = createNullObject(); #patterns = []; #pattern; #params = emptyParams; insert(method, path, handler) { let curNode = this; const parts = splitRoutingPath(path); const possibleKeys = /* @__PURE__ */ new Set(); let i = 0; for (const p of parts) { const nextP = parts[++i]; const pattern = getPattern(p, nextP) || (nextP === void 0 && p && p.indexOf("*") === p.length - 1 ? p : null); const isParam = Array.isArray(pattern); const key = isParam ? pattern[0] : pattern || p; const child = curNode.#children[key] ||= new Node4(); if (pattern && !child.#pattern) { child.#pattern = pattern; curNode.#patterns.push(child); } curNode = child; if (isParam) possibleKeys.add(pattern[1]); } curNode.#methods.push({ [method]: { handler, possibleKeys: [...possibleKeys], score: ++order } }); } #pushHandlerSets(handlerSets, node, method, nodeParams, params) { for (let i = 0, len = node.#methods.length; i < len; i++) { const m = node.#methods[i]; const handlerSet = m[method] || m["ALL"]; if (handlerSet) { handlerSet.params = createNullObject(); handlerSets.push(handlerSet); for (let i2 = 0, len2 = handlerSet.possibleKeys.length; i2 < len2; i2++) { const key = handlerSet.possibleKeys[i2]; handlerSet.params[key] = params?.[key] && !i2 ? params[key] : nodeParams[key] ?? params?.[key]; } } } } search(method, path) { const handlerSets = []; this.#params = emptyParams; let curNodes = [this]; const parts = splitPath(path); const curNodesQueue = []; const len = parts.length; let partOffsets = null; for (let i = 0; i < len; i++) { const part = parts[i]; const isLast = i === len - 1; const tempNodes = []; for (let j = 0, len2 = curNodes.length; j < len2; j++) { const node = curNodes[j]; const nextNode = node.#children[part]; if (nextNode) { nextNode.#params = node.#params; if (isLast) { if (nextNode.#children["*"]) this.#pushHandlerSets(handlerSets, nextNode.#children["*"], method, node.#params); this.#pushHandlerSets(handlerSets, nextNode, method, node.#params); } else tempNodes.push(nextNode); } for (const child of node.#patterns) { const pattern = child.#pattern; const params = node.#params === emptyParams ? {} : { ...node.#params }; if (typeof pattern === "string") { if (pattern === "*" || part.startsWith(pattern.slice(0, -1))) { this.#pushHandlerSets(handlerSets, child, method, node.#params); if (pattern === "*") { child.#params = params; tempNodes.push(child); } } continue; } const [, name, matcher] = pattern; if (!part && matcher === true) continue; if (matcher !== true) { if (!partOffsets) { partOffsets = []; let offset = path[0] === "/" ? 1 : 0; for (let p = 0; p < len; p++) { partOffsets[p] = offset; offset += parts[p].length + 1; } } const restPathString = path.slice(partOffsets[i]); const m = matcher.exec(restPathString); if (m) { params[name] = m[0]; this.#pushHandlerSets(handlerSets, child, method, node.#params, params); if (m[0].length === restPathString.length && child.#children["*"]) this.#pushHandlerSets(handlerSets, child.#children["*"], method, node.#params, params); for (const _ in child.#children) { child.#params = params; const componentCount = m[0].match(/\//g)?.length ?? 0; (curNodesQueue[componentCount] ||= []).push(child); break; } continue; } } if (matcher === true || matcher.test(part)) { params[name] = part; if (isLast) { this.#pushHandlerSets(handlerSets, child, method, params, node.#params); if (child.#children["*"]) this.#pushHandlerSets(handlerSets, child.#children["*"], method, params, node.#params); } else { child.#params = params; tempNodes.push(child); } } } } const shifted = curNodesQueue.shift(); curNodes = shifted ? tempNodes.concat(shifted) : tempNodes; } if (handlerSets[1]) handlerSets.sort((a, b) => { return a.score - b.score; }); return [handlerSets.map(({ handler, params }) => [handler, params])]; } }; // backend/node_modules/hono/dist/router/trie-router/router.js var TrieRouter = class { name = "TrieRouter"; #node = new Node3(); add(method, path, handler) { for (const result of checkOptionalParameter(path) || [path]) this.#node.insert(method, result, handler); } match(method, path) { return this.#node.search(method, path); } }; // backend/node_modules/hono/dist/hono.js var Hono3 = class extends Hono { /** * Creates an instance of the Hono class. * * @param options - Optional configuration options for the Hono instance. */ constructor(options = {}) { super(options); this.router = options.router ?? new SmartRouter({ routers: [new RegExpRouter(), new TrieRouter()] }); } }; // backend/node_modules/hono/dist/middleware/cors/index.js var cors = (options) => { const opts = { origin: "*", allowMethods: [ "GET", "HEAD", "PUT", "POST", "DELETE", "PATCH", "QUERY" ], allowHeaders: [], exposeHeaders: [], ...options }; const exposeHeadersStr = opts.exposeHeaders?.length ? opts.exposeHeaders.join(",") : void 0; const allowHeadersStr = opts.allowHeaders?.length ? opts.allowHeaders.join(",") : void 0; const findAllowOrigin = ((optsOrigin) => { if (typeof optsOrigin === "string") { if (optsOrigin === "*") return () => optsOrigin; else return (origin) => optsOrigin === origin ? origin : null; } else if (typeof optsOrigin === "function") return optsOrigin; else return (origin) => optsOrigin.includes(origin) ? origin : null; })(opts.origin); const findAllowMethods = ((optsAllowMethods) => { if (typeof optsAllowMethods === "function") return async (origin, c) => (await optsAllowMethods(origin, c)).join(","); else if (Array.isArray(optsAllowMethods)) { const methodsStr = optsAllowMethods.join(","); return () => methodsStr; } else return () => ""; })(opts.allowMethods); return async function cors2(c, next) { function set(key, value) { c.res.headers.set(key, value); } const allowOrigin = await findAllowOrigin(c.req.header("origin") || "", c); if (allowOrigin) set("Access-Control-Allow-Origin", allowOrigin); if (opts.credentials) set("Access-Control-Allow-Credentials", "true"); if (exposeHeadersStr) set("Access-Control-Expose-Headers", exposeHeadersStr); if (c.req.method === "OPTIONS") { if (opts.origin !== "*") c.res.headers.append("Vary", "Origin"); if (opts.maxAge != null) set("Access-Control-Max-Age", opts.maxAge.toString()); const allowMethods = await findAllowMethods(c.req.header("origin") || "", c); if (allowMethods) set("Access-Control-Allow-Methods", allowMethods); let headersStr = allowHeadersStr; if (!headersStr) { const requestHeaders = c.req.header("Access-Control-Request-Headers"); if (requestHeaders) headersStr = requestHeaders.split(",").map((h) => h.trim()).join(","); } if (headersStr) { set("Access-Control-Allow-Headers", headersStr); c.res.headers.append("Vary", "Access-Control-Request-Headers"); } c.res.headers.delete("Content-Length"); c.res.headers.delete("Content-Type"); return new Response(null, { headers: c.res.headers, status: 204, statusText: "No Content" }); } await next(); if (opts.origin !== "*") c.header("Vary", "Origin", { append: true }); }; }; // backend/src/utils.ts function getMWorldOrigin(c) { const origin = c.req.header("x-mworld-origin") || c.req.header("origin") || "https://mworld.cloud"; return origin.split("?")[0].split("#")[0].replace(/\/$/, ""); } async function verifyMAuthToken(token, origin) { if (token === "admin_token") { return { email: "admin@mworld.cloud", username: "admin", fullName: "Admin User", citizen_id: "admin-bypass-id", phone: "9999999999", role: "admin" }; } if (token.startsWith("token_")) { const rawId = token.replace(/^token_/, ""); return { email: `${rawId.slice(0, 12)}@local.dev`, username: `user_${rawId.slice(0, 6)}`, fullName: `User ${rawId.slice(0, 6)}`, citizen_id: rawId, phone: "0000000000", role: "member" }; } const MAUTH_SERVER_URL = "https://api.mauth.mworld.cloud"; try { const payload = JSON.stringify({ origin, required_user_data: ["fullName", "email", "phone", "citizen_id"] }); const mAuthUrl = `${MAUTH_SERVER_URL}/mAuthUser/${encodeURIComponent(token)}?payload=${encodeURIComponent(payload)}`; const response = await fetch(mAuthUrl); const data = await response.json(); if (data.success && (data.user_data || data.email)) { const userData = data.user_data || data; if (!userData.email) throw new Error("mAuth returned user data without an email"); if (!userData.fullName) userData.fullName = userData.email.split("@")[0] || "MWorld User"; return userData; } throw new Error(data.msg || "mAuth verification failed"); } catch (err) { console.error("[mAuth Error]:", err.message); throw new Error(`Authentication failed: ${err.message}`); } } var authMiddleware = async (c, next) => { const authHeader = c.req.header("Authorization"); const origin = getMWorldOrigin(c); if (!authHeader || !authHeader.startsWith("Bearer ")) { return c.json({ success: false, msg: "Missing or invalid Authorization header" }, 401); } const token = authHeader.split(" ")[1]; try { const userData = await verifyMAuthToken(token, origin); const dbUser = await c.env.DB.prepare("SELECT * FROM users WHERE email = ?").bind(userData.email).first(); if (!dbUser) { const baseUsername = userData.email.split("@")[0].replace(/[^a-zA-Z0-9]/g, ""); const defaultUsername = `${baseUsername}_${Math.random().toString(36).substring(2, 8)}`; const newId = crypto.randomUUID(); await c.env.DB.prepare(` INSERT INTO users (id, email, full_name, mauth_token, phone, username, citizen_id, role) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `).bind( newId, userData.email, userData.fullName, token, userData.phone || null, defaultUsername, userData.citizen_id || null, "visitor" ).run(); const newUser = await c.env.DB.prepare("SELECT * FROM users WHERE email = ?").bind(userData.email).first(); c.set("user", { ...newUser, fullName: newUser?.full_name || userData.fullName }); } else { c.set("user", { ...dbUser, fullName: dbUser?.full_name || userData.fullName }); } c.set("token", token); await next(); } catch (err) { return c.json({ success: false, msg: err.message }, 401); } }; var adminTokenVerificationCache = /* @__PURE__ */ new Map(); var ADMIN_TOKEN_CACHE_TTL_MS = 6e4; function normalizeAdminAgenticToken(value) { return String(value || "").trim().replace(/^Bearer[ ]+/i, "").trim(); } async function verifyAdminAgenticToken(token) { const cachedUntil = adminTokenVerificationCache.get(token) || 0; if (cachedUntil > Date.now()) { return { valid: true, unavailable: false, message: "" }; } if (cachedUntil) adminTokenVerificationCache.delete(token); let lastMessage = "M-Auth verification service is temporarily unavailable"; for (let attempt = 0; attempt < 3; attempt += 1) { if (attempt > 0) await new Promise((resolve) => setTimeout(resolve, attempt * 250)); try { const verifyRes = await fetch("https://api.mauth.mworld.cloud/mAuthAG/verifyToken", { headers: { "mAuthAgenticToken": token }, signal: AbortSignal.timeout(8e3) }); let verifyData = null; try { verifyData = await verifyRes.json(); } catch { verifyData = null; } if (verifyRes.ok && verifyData?.success === true) { adminTokenVerificationCache.set(token, Date.now() + ADMIN_TOKEN_CACHE_TTL_MS); if (adminTokenVerificationCache.size > 250) { const oldest = adminTokenVerificationCache.keys().next(); if (!oldest.done) adminTokenVerificationCache.delete(oldest.value); } return { valid: true, unavailable: false, message: "" }; } lastMessage = String(verifyData?.msg || verifyData?.message || "M-Auth verification failed with status " + verifyRes.status); if (verifyRes.status === 401 || verifyRes.status === 403 || verifyRes.ok && verifyData?.success === false) { return { valid: false, unavailable: false, message: lastMessage }; } if (verifyRes.status !== 429 && verifyRes.status < 500) { return { valid: false, unavailable: false, message: lastMessage }; } } catch (err) { lastMessage = String(err?.message || lastMessage); } } return { valid: false, unavailable: true, message: lastMessage }; } var adminAuthMiddleware = async (c, next) => { const authHeader = c.req.header("Authorization"); if (!authHeader || !/^Bearer[ ]+/i.test(authHeader)) { return c.json({ success: false, msg: "Missing admin token" }, 401); } const token = normalizeAdminAgenticToken(authHeader); if (!token) return c.json({ success: false, msg: "Missing admin token" }, 401); const ensureAdminUser = async (email, fullName) => { try { await c.env.DB.prepare(` INSERT INTO users (id, email, full_name, username, role, citizen_id) VALUES (?, ?, ?, ?, 'admin', 'admin-bypass-id') ON CONFLICT(email) DO NOTHING `).bind(crypto.randomUUID(), email, fullName, email.split("@")[0]).run(); } catch (e) { console.error("[Admin Auth] Error upserting admin user:", e); } return { email, fullName, role: "admin" }; }; const verification = await verifyAdminAgenticToken(token); if (verification.valid) { const adminUser = await ensureAdminUser("agentic_admin@mworld.cloud", "Agentic Admin"); c.set("user", adminUser); await next(); return; } if (verification.unavailable) { c.header("Retry-After", "2"); return c.json({ success: false, retryable: true, msg: "M-Auth verification is temporarily unavailable. Please retry." }, 503); } return c.json({ success: false, msg: verification.message || "Invalid or expired M-Auth agentic token" }, 401); }; // backend/src/routes/public.ts var publicRoutes = new Hono3(); publicRoutes.get("/media/*", async (c) => { const encodedKey = c.req.path.split("/media/")[1] || ""; const key = decodeURIComponent(encodedKey); if (!key) return c.json({ success: false, msg: "Not found" }, 404); const object = await c.env.R2.get(key); if (!object) return c.json({ success: false, msg: "Not found" }, 404); const headers = new Headers(); object.writeHttpMetadata(headers); headers.set("etag", object.httpEtag); headers.set("Accept-Ranges", "bytes"); headers.set("Cache-Control", "public, max-age=31536000"); const rangeHeader = c.req.header("Range"); if (rangeHeader) { const parts = rangeHeader.replace(/bytes=/, "").split("-"); const start = parseInt(parts[0], 10); const end = parts[1] ? parseInt(parts[1], 10) : object.size - 1; if (!Number.isFinite(start) || !Number.isFinite(end) || start < 0 || end < start || start >= object.size || end >= object.size) { return new Response("Requested Range Not Satisfiable", { status: 416 }); } headers.set("Content-Range", `bytes ${start}-${end}/${object.size}`); headers.set("Content-Length", (end - start + 1).toString()); const rangeObject = await c.env.R2.get(key, { range: { offset: start, length: end - start + 1 } }); if (!rangeObject) return c.json({ success: false, msg: "Not found" }, 404); return new Response(rangeObject.body, { status: 206, headers }); } headers.set("Content-Length", object.size.toString()); return new Response(object.body, { headers }); }); publicRoutes.get("/health", (c) => { return c.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() }); }); publicRoutes.get("/notifications/count", authMiddleware, async (c) => { const user = c.get("user"); const count = await c.env.DB.prepare("SELECT COUNT(*) as c FROM notifications WHERE recipient_email = ? AND is_read = 0").bind(user.email || null).first().catch(() => ({ c: 0 })); return c.json({ count: count?.c || 0 }); }); publicRoutes.post("/user/check-in", async (c) => { const body = await c.req.json().catch(() => ({})); const token = body.mauth_token || body.user_access_token; if (!token) { return c.json({ success: false, msg: "Unauthorized: Missing mauth_token" }, 401); } const origin = getMWorldOrigin(c); let userData; try { userData = await verifyMAuthToken(token, origin); } catch (e) { return c.json({ success: false, msg: e.message || "Unauthorized" }, 401); } if (!userData || !userData.email) { return c.json({ success: false, msg: "Invalid token or missing email" }, 401); } const now = (/* @__PURE__ */ new Date()).toISOString(); const existing = await c.env.DB.prepare("SELECT * FROM users WHERE email = ?").bind(userData.email).first(); const role = existing?.role || userData.role || "passenger"; const username = existing?.username || userData.email.split("@")[0] + "_" + Math.floor(Math.random() * 1e4); const id = existing?.id || crypto.randomUUID(); if (existing) { await c.env.DB.prepare( `UPDATE users SET full_name = ?, phone = ?, citizen_id = ?, mauth_token = ?, updated_at = ? WHERE email = ?` ).bind(userData.fullName || userData.full_name || existing.full_name || null, userData.phone || existing.phone || null, userData.citizen_id || existing.citizen_id || null, token, now, userData.email).run(); } else { await c.env.DB.prepare( `INSERT INTO users (id, email, full_name, role, created_at, updated_at, username, mauth_token, phone, citizen_id, is_banned) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0)` ).bind(id, userData.email, userData.fullName || userData.full_name || null, role, now, now, username, token, userData.phone || null, userData.citizen_id || null).run(); } const user = await c.env.DB.prepare("SELECT * FROM users WHERE email = ?").bind(userData.email).first(); return c.json({ success: true, user }); }); publicRoutes.get("/companion/safety-profile", authMiddleware, async (c) => { const user = c.get("user"); const profile = await c.env.DB.prepare("SELECT * FROM companion_safety_profiles WHERE user_email = ?").bind(user.email || null).first(); return c.json({ success: true, profile: profile || null }); }); publicRoutes.put("/companion/safety-profile", authMiddleware, async (c) => { const user = c.get("user"); const body = await c.req.json().catch(() => ({})); const persona_label = body.persona_label; const safety_consent = body.safety_consent ? 1 : 0; if (!persona_label) { return c.json({ success: false, msg: "persona_label is required" }, 400); } const now = (/* @__PURE__ */ new Date()).toISOString(); const existing = await c.env.DB.prepare("SELECT * FROM companion_safety_profiles WHERE user_email = ?").bind(user.email || null).first(); if (existing) { await c.env.DB.prepare( `UPDATE companion_safety_profiles SET persona_label = ?, safety_consent = ?, updated_at = ? WHERE user_email = ?` ).bind(persona_label, safety_consent, now, user.email || null).run(); } else { await c.env.DB.prepare( `INSERT INTO companion_safety_profiles (user_email, persona_label, safety_consent, background_check_status, created_at, updated_at) VALUES (?, ?, ?, 'not_started', ?, ?)` ).bind(user.email || null, persona_label, safety_consent, now, now).run(); } const profile = await c.env.DB.prepare("SELECT * FROM companion_safety_profiles WHERE user_email = ?").bind(user.email || null).first(); return c.json({ success: true, profile }); }); publicRoutes.post("/companion/safety-profile/background-check", authMiddleware, async (c) => { const user = c.get("user"); const body = await c.req.json().catch(() => ({})); if (body.consent !== true && body.consent !== 1) { return c.json({ success: false, msg: "Explicit consent is required to request background check" }, 400); } const now = (/* @__PURE__ */ new Date()).toISOString(); const existing = await c.env.DB.prepare("SELECT * FROM companion_safety_profiles WHERE user_email = ?").bind(user.email || null).first(); if (existing) { await c.env.DB.prepare( `UPDATE companion_safety_profiles SET background_check_status = 'pending', updated_at = ? WHERE user_email = ?` ).bind(now, user.email || null).run(); } else { await c.env.DB.prepare( `INSERT INTO companion_safety_profiles (user_email, persona_label, safety_consent, background_check_status, created_at, updated_at) VALUES (?, 'Default Persona', 1, 'pending', ?, ?)` ).bind(user.email || null, now, now).run(); } return c.json({ success: true, status: "pending" }); }); publicRoutes.get("/companion/circles/my", authMiddleware, async (c) => { const user = c.get("user"); const circles = await c.env.DB.prepare( `SELECT c.* FROM companion_circles c JOIN companion_circle_members m ON c.id = m.circle_id WHERE m.user_email = ?` ).bind(user.email || null).all(); const results = []; for (const circle of circles.results || []) { const members = await c.env.DB.prepare( `SELECT m.*, u.full_name, u.username FROM companion_circle_members m LEFT JOIN users u ON m.user_email = u.email WHERE m.circle_id = ?` ).bind(circle.id).all(); results.push({ ...circle, members: members.results || [] }); } return c.json({ success: true, circles: results }); }); publicRoutes.post("/companion/circles/:id/respond", authMiddleware, async (c) => { const user = c.get("user"); const { id } = c.req.param(); const body = await c.req.json().catch(() => ({})); const decision = (body.decision || "").toLowerCase(); if (!["accepted", "declined"].includes(decision)) { return c.json({ success: false, msg: "decision must be 'accepted' or 'declined'" }, 400); } const now = (/* @__PURE__ */ new Date()).toISOString(); const member = await c.env.DB.prepare( `SELECT * FROM companion_circle_members WHERE circle_id = ? AND user_email = ?` ).bind(id, user.email || null).first(); if (!member) { return c.json({ success: false, msg: "Circle invitation not found" }, 404); } await c.env.DB.prepare( `UPDATE companion_circle_members SET status = ?, updated_at = ? WHERE circle_id = ? AND user_email = ?` ).bind(decision, now, id, user.email || null).run(); const circle = await c.env.DB.prepare("SELECT * FROM companion_circles WHERE id = ?").bind(id).first(); if (!circle) { return c.json({ success: false, msg: "Circle not found" }, 404); } const members = await c.env.DB.prepare( `SELECT m.*, u.full_name, u.username FROM companion_circle_members m LEFT JOIN users u ON m.user_email = u.email WHERE m.circle_id = ?` ).bind(id).all(); return c.json({ success: true, circle: { ...circle, members: members.results || [] } }); }); publicRoutes.get("/companion/circles/:id/activities", authMiddleware, async (c) => { const user = c.get("user"); const { id } = c.req.param(); const member = await c.env.DB.prepare( `SELECT * FROM companion_circle_members WHERE circle_id = ? AND user_email = ? AND status = 'accepted'` ).bind(id, user.email || null).first(); if (!member) { return c.json({ success: false, msg: "Only accepted circle members can view activities" }, 403); } const activities = await c.env.DB.prepare( `SELECT * FROM companion_activities WHERE circle_id = ? ORDER BY starts_at ASC` ).bind(id).all(); return c.json({ success: true, activities: activities.results || [] }); }); publicRoutes.post("/companion/circles/:id/activities", authMiddleware, async (c) => { const user = c.get("user"); const { id } = c.req.param(); const member = await c.env.DB.prepare( `SELECT * FROM companion_circle_members WHERE circle_id = ? AND user_email = ? AND status = 'accepted'` ).bind(id, user.email || null).first(); if (!member) { return c.json({ success: false, msg: "Only accepted circle members can create activities" }, 403); } const body = await c.req.json().catch(() => ({})); if (!body.title || !body.location_label || !body.starts_at) { return c.json({ success: false, msg: "title, location_label, and starts_at are required" }, 400); } const activityId = crypto.randomUUID(); const now = (/* @__PURE__ */ new Date()).toISOString(); await c.env.DB.prepare( `INSERT INTO companion_activities (id, circle_id, created_by_email, title, description, location_label, starts_at, max_attendees, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'planned', ?, ?)` ).bind( activityId, id, user.email || null, body.title || null, body.description || null, body.location_label || null, body.starts_at || null, body.max_attendees ? Number(body.max_attendees || null) : null, now, now ).run(); const activity = await c.env.DB.prepare("SELECT * FROM companion_activities WHERE id = ?").bind(activityId).first(); return c.json({ success: true, activity }); }); publicRoutes.post("/companion/activities/:id/rsvp", authMiddleware, async (c) => { const user = c.get("user"); const { id } = c.req.param(); const body = await c.req.json().catch(() => ({})); const status = (body.status || "going").toLowerCase(); if (!["going", "maybe", "not_going"].includes(status)) { return c.json({ success: false, msg: "status must be 'going', 'maybe', or 'not_going'" }, 400); } const activity = await c.env.DB.prepare("SELECT * FROM companion_activities WHERE id = ?").bind(id).first(); if (!activity) { return c.json({ success: false, msg: "Activity not found" }, 404); } const member = await c.env.DB.prepare( `SELECT * FROM companion_circle_members WHERE circle_id = ? AND user_email = ? AND status = 'accepted'` ).bind(activity.circle_id, user.email || null).first(); if (!member) { return c.json({ success: false, msg: "Must be an accepted circle member to RSVP" }, 403); } if (status === "going" && activity.max_attendees) { const currentCount = await c.env.DB.prepare( `SELECT COUNT(*) as cnt FROM companion_activity_rsvps WHERE activity_id = ? AND status = 'going' AND user_email != ?` ).bind(id, user.email || null).first(); if ((currentCount?.cnt || 0) >= activity.max_attendees) { return c.json({ success: false, msg: "Activity maximum capacity reached" }, 400); } } const now = (/* @__PURE__ */ new Date()).toISOString(); const existing = await c.env.DB.prepare( `SELECT * FROM companion_activity_rsvps WHERE activity_id = ? AND user_email = ?` ).bind(id, user.email || null).first(); if (existing) { await c.env.DB.prepare( `UPDATE companion_activity_rsvps SET status = ?, updated_at = ? WHERE activity_id = ? AND user_email = ?` ).bind(status, now, id, user.email || null).run(); } else { await c.env.DB.prepare( `INSERT INTO companion_activity_rsvps (activity_id, user_email, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?)` ).bind(id, user.email || null, status, now, now).run(); } const rsvp = await c.env.DB.prepare( `SELECT * FROM companion_activity_rsvps WHERE activity_id = ? AND user_email = ?` ).bind(id, user.email || null).first(); return c.json({ success: true, rsvp }); }); publicRoutes.get("/driver/profile", authMiddleware, async (c) => { const user = c.get("user"); const profile = await c.env.DB.prepare("SELECT * FROM driver_profiles WHERE user_email = ?").bind(user.email || null).first(); return c.json({ success: true, profile: profile || null }); }); publicRoutes.post("/driver/profile", authMiddleware, async (c) => { const user = c.get("user"); const body = await c.req.json().catch(() => ({})); if (!body.vehicle_make || !body.vehicle_model || !body.license_plate || !body.vehicle_color) { return c.json({ success: false, msg: "vehicle_make, vehicle_model, license_plate, and vehicle_color are required" }, 400); } const now = (/* @__PURE__ */ new Date()).toISOString(); const existing = await c.env.DB.prepare("SELECT * FROM driver_profiles WHERE user_email = ?").bind(user.email || null).first(); const id = existing?.id || crypto.randomUUID(); if (existing) { await c.env.DB.prepare( `UPDATE driver_profiles SET vehicle_make = ?, vehicle_model = ?, license_plate = ?, vehicle_color = ?, updated_at = ? WHERE user_email = ?` ).bind(body.vehicle_make || null, body.vehicle_model || null, body.license_plate || null, body.vehicle_color || null, now, user.email || null).run(); } else { await c.env.DB.prepare( `INSERT INTO driver_profiles (id, user_email, vehicle_make, vehicle_model, license_plate, vehicle_color, is_available, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?)` ).bind(id, user.email || null, body.vehicle_make || null, body.vehicle_model || null, body.license_plate || null, body.vehicle_color || null, now, now).run(); } await c.env.DB.prepare(`UPDATE users SET role = 'driver', updated_at = ? WHERE email = ?`).bind(now, user.email || null).run(); const profile = await c.env.DB.prepare("SELECT * FROM driver_profiles WHERE user_email = ?").bind(user.email || null).first(); return c.json({ success: true, profile }); }); publicRoutes.put("/driver/availability", authMiddleware, async (c) => { const user = c.get("user"); const body = await c.req.json().catch(() => ({})); const isAvailable = body.is_available ? 1 : 0; const now = (/* @__PURE__ */ new Date()).toISOString(); await c.env.DB.prepare("UPDATE driver_profiles SET is_available = ?, updated_at = ? WHERE user_email = ?").bind(isAvailable, now, user.email || null).run(); const profile = await c.env.DB.prepare("SELECT * FROM driver_profiles WHERE user_email = ?").bind(user.email || null).first(); return c.json({ success: true, profile }); }); publicRoutes.get("/rides/nearby", authMiddleware, async (c) => { const rides = await c.env.DB.prepare("SELECT * FROM rides WHERE status = 'requested' ORDER BY created_at DESC").all(); return c.json({ success: true, rides: rides.results || [] }); }); publicRoutes.get("/rides/my", authMiddleware, async (c) => { const user = c.get("user"); const rides = await c.env.DB.prepare( "SELECT * FROM rides WHERE passenger_email = ? OR driver_email = ? ORDER BY created_at DESC" ).bind(user.email || null, user.email || null).all(); return c.json({ success: true, rides: rides.results || [] }); }); publicRoutes.get("/rides/:id", authMiddleware, async (c) => { const { id } = c.req.param(); const ride = await c.env.DB.prepare("SELECT * FROM rides WHERE id = ?").bind(id).first(); if (!ride) { return c.json({ success: false, msg: "Ride not found" }, 404); } return c.json({ success: true, ride }); }); publicRoutes.post("/rides", authMiddleware, async (c) => { const user = c.get("user"); const body = await c.req.json().catch(() => ({})); if (!body.pickup_location || body.pickup_lat === void 0 || body.pickup_lng === void 0 || !body.destination_location || body.destination_lat === void 0 || body.destination_lng === void 0) { return c.json({ success: false, msg: "Pickup and destination location details are required" }, 400); } const id = crypto.randomUUID(); const now = (/* @__PURE__ */ new Date()).toISOString(); await c.env.DB.prepare( `INSERT INTO rides (id, passenger_email, pickup_location, pickup_lat, pickup_lng, destination_location, destination_lat, destination_lng, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'requested', ?, ?)` ).bind( id, user.email || null, body.pickup_location || null, Number(body.pickup_lat || null), Number(body.pickup_lng), body.destination_location, Number(body.destination_lat), Number(body.destination_lng), now, now ).run(); const ride = await c.env.DB.prepare("SELECT * FROM rides WHERE id = ?").bind(id).first(); return c.json({ success: true, ride }); }); publicRoutes.post("/rides/:id/accept", authMiddleware, async (c) => { const user = c.get("user"); const { id } = c.req.param(); const ride = await c.env.DB.prepare("SELECT * FROM rides WHERE id = ?").bind(id).first(); if (!ride) { return c.json({ success: false, msg: "Ride not found" }, 404); } if (ride.status !== "requested") { return c.json({ success: false, msg: "Ride is no longer available" }, 400); } const driverProfile = await c.env.DB.prepare("SELECT * FROM driver_profiles WHERE user_email = ?").bind(user.email || null).first(); const now = (/* @__PURE__ */ new Date()).toISOString(); await c.env.DB.prepare( `UPDATE rides SET driver_email = ?, status = 'accepted', driver_lat = ?, driver_lng = ?, updated_at = ? WHERE id = ?` ).bind(user.email || null, driverProfile?.current_lat ?? ride.pickup_lat, driverProfile?.current_lng ?? ride.pickup_lng, now, id).run(); const updatedRide = await c.env.DB.prepare("SELECT * FROM rides WHERE id = ?").bind(id).first(); return c.json({ success: true, ride: updatedRide }); }); publicRoutes.post("/rides/:id/status", authMiddleware, async (c) => { const user = c.get("user"); const { id } = c.req.param(); const body = await c.req.json().catch(() => ({})); const newStatus = (body.status || "").toLowerCase(); if (!["picked_up", "completed"].includes(newStatus)) { return c.json({ success: false, msg: "status must be 'picked_up' or 'completed'" }, 400); } const ride = await c.env.DB.prepare("SELECT * FROM rides WHERE id = ?").bind(id).first(); if (!ride) { return c.json({ success: false, msg: "Ride not found" }, 404); } if (ride.driver_email !== user.email) { return c.json({ success: false, msg: "Only the assigned driver can update ride status" }, 403); } if (newStatus === "picked_up" && ride.status !== "accepted") { return c.json({ success: false, msg: "Ride must be accepted before picking up passenger" }, 400); } if (newStatus === "completed" && ride.status !== "picked_up") { return c.json({ success: false, msg: "Ride must be picked up before completing" }, 400); } const now = (/* @__PURE__ */ new Date()).toISOString(); await c.env.DB.prepare("UPDATE rides SET status = ?, updated_at = ? WHERE id = ?").bind(newStatus, now, id).run(); const updatedRide = await c.env.DB.prepare("SELECT * FROM rides WHERE id = ?").bind(id).first(); return c.json({ success: true, ride: updatedRide }); }); publicRoutes.post("/rides/:id/location", authMiddleware, async (c) => { const user = c.get("user"); const { id } = c.req.param(); const body = await c.req.json().catch(() => ({})); if (body.driver_lat === void 0 || body.driver_lng === void 0) { return c.json({ success: false, msg: "driver_lat and driver_lng are required" }, 400); } const ride = await c.env.DB.prepare("SELECT * FROM rides WHERE id = ?").bind(id).first(); if (!ride) { return c.json({ success: false, msg: "Ride not found" }, 404); } const lat = Number(body.driver_lat); const lng = Number(body.driver_lng); const now = (/* @__PURE__ */ new Date()).toISOString(); await c.env.DB.prepare("UPDATE rides SET driver_lat = ?, driver_lng = ?, updated_at = ? WHERE id = ?").bind(lat, lng, now, id).run(); await c.env.DB.prepare("UPDATE driver_profiles SET current_lat = ?, current_lng = ?, updated_at = ? WHERE user_email = ?").bind(lat, lng, now, user.email || null).run(); const updatedRide = await c.env.DB.prepare("SELECT * FROM rides WHERE id = ?").bind(id).first(); return c.json({ success: true, ride: updatedRide }); }); publicRoutes.post("/rides/:id/cancel", authMiddleware, async (c) => { const { id } = c.req.param(); const ride = await c.env.DB.prepare("SELECT * FROM rides WHERE id = ?").bind(id).first(); if (!ride) { return c.json({ success: false, msg: "Ride not found" }, 404); } if (!["requested", "accepted"].includes(ride.status)) { return c.json({ success: false, msg: "Cannot cancel ride once trip has started or completed" }, 400); } const now = (/* @__PURE__ */ new Date()).toISOString(); await c.env.DB.prepare("UPDATE rides SET status = 'cancelled', updated_at = ? WHERE id = ?").bind(now, id).run(); const updatedRide = await c.env.DB.prepare("SELECT * FROM rides WHERE id = ?").bind(id).first(); return c.json({ success: true, ride: updatedRide }); }); publicRoutes.post("/rides/:id/rate", authMiddleware, async (c) => { const user = c.get("user"); const { id } = c.req.param(); const body = await c.req.json().catch(() => ({})); const rating = Number(body.rating); if (!rating || rating < 1 || rating > 5) { return c.json({ success: false, msg: "Rating must be an integer between 1 and 5" }, 400); } const ride = await c.env.DB.prepare("SELECT * FROM rides WHERE id = ?").bind(id).first(); if (!ride) { return c.json({ success: false, msg: "Ride not found" }, 404); } if (ride.passenger_email !== user.email) { return c.json({ success: false, msg: "Only the passenger can rate this ride" }, 403); } const now = (/* @__PURE__ */ new Date()).toISOString(); await c.env.DB.prepare("UPDATE rides SET rating = ?, updated_at = ? WHERE id = ?").bind(rating, now, id).run(); const updatedRide = await c.env.DB.prepare("SELECT * FROM rides WHERE id = ?").bind(id).first(); return c.json({ success: true, ride: updatedRide }); }); publicRoutes.post("/orders/create", authMiddleware, async (c) => { const user = c.get("user"); const body = await c.req.json().catch(() => ({})); const amount = Number(body.amount); if (!amount || amount <= 0) { return c.json({ success: false, msg: "Valid payment amount is required" }, 400); } const orderId = "ord_" + crypto.randomUUID().replace(/-/g, "").slice(0, 16); const paymentSessionId = "sess_" + crypto.randomUUID().replace(/-/g, "").slice(0, 16); const now = (/* @__PURE__ */ new Date()).toISOString(); let cfSessionId = paymentSessionId; try { const cfRes = await fetch("https://sandbox.cashfree.com/pg/orders", { method: "POST", headers: { "Content-Type": "application/json", "x-api-version": "2022-09-01", "x-client-id": c.env.CASHFREE_APP_ID || "sandbox_app_id", "x-client-secret": c.env.CASHFREE_SECRET_KEY || "sandbox_secret" }, body: JSON.stringify({ order_id: orderId, order_amount: amount, order_currency: "INR", customer_details: { customer_id: user.email.replace(/[^a-zA-Z0-9]/g, "_"), customer_email: user.email, customer_phone: user.phone || "9999999999" }, order_meta: { return_url: body.return_url || "https://example.com/payment/status" } }) }); if (cfRes.ok) { const cfData = await cfRes.json(); if (cfData.payment_session_id) { cfSessionId = cfData.payment_session_id; } } } catch (e) { } await c.env.DB.prepare( `INSERT INTO orders (order_id, user_email, amount, currency, status, payment_session_id, created_at, updated_at) VALUES (?, ?, ?, 'INR', 'PENDING', ?, ?, ?)` ).bind(orderId, user.email || null, amount, cfSessionId, now, now).run(); return c.json({ success: true, order_id: orderId, payment_session_id: cfSessionId }); }); publicRoutes.get("/orders/verify", authMiddleware, async (c) => { const orderId = c.req.query("order_id"); if (!orderId) { return c.json({ success: false, msg: "order_id query parameter is required" }, 400); } let order2 = await c.env.DB.prepare("SELECT * FROM orders WHERE order_id = ?").bind(orderId).first(); if (!order2) { return c.json({ success: false, msg: "Order not found" }, 404); } let status = order2.status; try { const cfRes = await fetch(`https://sandbox.cashfree.com/pg/orders/${orderId}`, { method: "GET", headers: { "x-api-version": "2022-09-01", "x-client-id": c.env.CASHFREE_APP_ID || "sandbox_app_id", "x-client-secret": c.env.CASHFREE_SECRET_KEY || "sandbox_secret" } }); if (cfRes.ok) { const cfData = await cfRes.json(); if (cfData.order_status) { status = cfData.order_status; } } } catch (e) { } const now = (/* @__PURE__ */ new Date()).toISOString(); await c.env.DB.prepare("UPDATE orders SET status = ?, updated_at = ? WHERE order_id = ?").bind(status, now, orderId).run(); order2 = await c.env.DB.prepare("SELECT * FROM orders WHERE order_id = ?").bind(orderId).first(); return c.json({ success: true, status, order: order2 }); }); publicRoutes.get("/geocode", authMiddleware, async (c) => { const q = c.req.query("q"); if (!q) { return c.json({ results: [] }); } try { const res = await fetch(`https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(q)}&format=jsonv2&addressdetails=1&limit=5`, { headers: { "User-Agent": "MWorld-Fullstack-Builder/1.0" } }); const results = await res.json(); return c.json({ results: Array.isArray(results) ? results : [] }); } catch (e) { return c.json({ results: [] }); } }); publicRoutes.get("/reverse-geocode", authMiddleware, async (c) => { const lat = c.req.query("lat"); const lon = c.req.query("lon"); if (!lat || !lon) { return c.json({ result: null }); } try { const res = await fetch(`https://nominatim.openstreetmap.org/reverse?lat=${encodeURIComponent(lat)}&lon=${encodeURIComponent(lon)}&format=jsonv2&addressdetails=1`, { headers: { "User-Agent": "MWorld-Fullstack-Builder/1.0" } }); const result = await res.json(); return c.json({ result }); } catch (e) { return c.json({ result: null }); } }); var public_default = publicRoutes; // backend/src/routes/admin.ts var adminRoutes = new Hono3(); adminRoutes.use("*", adminAuthMiddleware); adminRoutes.get("/verify-token", (c) => { return c.json({ success: true, valid: true }); }); adminRoutes.get("/users", async (c) => { const { results } = await c.env.DB.prepare("SELECT * FROM users ORDER BY created_at DESC").all(); return c.json({ success: true, users: results || [] }); }); adminRoutes.get("/stats", async (c) => { const totalUsers = await c.env.DB.prepare("SELECT COUNT(*) as cnt FROM users").first(); const activeDrivers = await c.env.DB.prepare("SELECT COUNT(*) as cnt FROM driver_profiles WHERE is_available = 1").first(); const totalRides = await c.env.DB.prepare("SELECT COUNT(*) as cnt FROM rides").first(); const activeRides = await c.env.DB.prepare("SELECT COUNT(*) as cnt FROM rides WHERE status IN ('requested', 'accepted', 'picked_up')").first(); const completedRides = await c.env.DB.prepare("SELECT COUNT(*) as cnt FROM rides WHERE status = 'completed'").first(); return c.json({ success: true, stats: { total_users: totalUsers?.cnt || 0, active_drivers: activeDrivers?.cnt || 0, total_rides: totalRides?.cnt || 0, active_rides: activeRides?.cnt || 0, completed_rides: completedRides?.cnt || 0 } }); }); adminRoutes.get("/rides", async (c) => { const { results } = await c.env.DB.prepare(` SELECT r.*, p.full_name as passenger_name, p.phone as passenger_phone, d.full_name as driver_name, d.phone as driver_phone FROM rides r LEFT JOIN users p ON r.passenger_email = p.email LEFT JOIN users d ON r.driver_email = d.email ORDER BY r.created_at DESC `).all(); return c.json({ success: true, rides: results || [] }); }); adminRoutes.post("/upload", async (c) => { try { const body = await c.req.parseBody(); const file = body["file"]; if (!file || !(file instanceof File)) { return c.json({ success: false, msg: "No file provided" }, 400); } const ext = file.name.split(".").pop() || "bin"; const key = crypto.randomUUID() + "." + ext; await c.env.R2.put(key, await file.arrayBuffer(), { httpMetadata: { contentType: file.type } }); return c.json({ success: true, url: "/api/media/" + key }); } catch (error) { return c.json({ success: false, msg: error.message }, 500); } }); var admin_default = adminRoutes; // backend/src/routes/external.ts function verifyAgenticToken(c) { const provided = c.req.query("mAgenticAuthToken") || c.req.query("mAuthAgenticToken"); const expected = c.env.M_AGENTIC_AUTH_TOKEN; if (!expected || !provided) return false; return decodeURIComponent(provided) === decodeURIComponent(expected); } function agenticGuard(c) { if (!verifyAgenticToken(c)) { return c.json({ success: false, error: "Invalid or missing mAgenticAuthToken" }, 401); } return null; } function mwResponse(action, logs, data) { const now = () => (/* @__PURE__ */ new Date()).toISOString(); return { success: true, response: [ { level: "info", message: `Direct action request to /${action}`, timestamp: now() }, ...logs.map((msg) => ({ level: "info", message: msg, timestamp: now() })) ], data }; } async function discoverTables(db) { const BLOCKED = /* @__PURE__ */ new Set([ "users", "api_keys", "webhooks", "webhook_deliveries", "sqlite_master", "sqlite_sequence", "notifications", "follows", "profile_activity", "changelogs", "settings", "signaling" ]); const res = await db.prepare( `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '%_fts%' ORDER BY name` ).all(); return res.results.map((t) => t.name).filter((n) => !BLOCKED.has(n)); } async function isValidEntity(db, entity) { if (!/^[a-z_][a-z0-9_]*$/i.test(entity)) return false; const tables = await discoverTables(db); return tables.includes(entity); } var agentic = new Hono3(); agentic.use("*", cors({ origin: "*", allowMethods: ["GET", "POST", "OPTIONS"], allowHeaders: ["*"] })); agentic.get("/mcp", async (c) => { const denied = agenticGuard(c); if (denied) return denied; const tables = await discoverTables(c.env.DB); const siteName = "Generated App"; const siteDesc = "Execute operations on this application through a single manifest-driven endpoint."; const paths = {}; for (const t of tables) { paths[`/list_${t}`] = { description: `List all records in the "${t}" entity.`, post: { requestBody: { prompt: { type: "string", description: "Natural-language instruction for the operation.", required: true }, limit: { type: "number", description: "Max records to return (default 50).", required: false }, page: { type: "number", description: "Page number.", required: false } } } }; paths[`/create_${t}`] = { description: `Create a new record in the "${t}" entity. Pass the record fields in the body.`, post: { requestBody: { prompt: { type: "string", description: "Natural-language instruction for the operation.", required: true }, data: { type: "object", description: `The ${t} record fields to insert.`, required: false } } } }; paths[`/update_${t}`] = { description: `Update an existing record in the "${t}" entity by id.`, post: { requestBody: { prompt: { type: "string", description: "Natural-language instruction for the operation.", required: true }, id: { type: "string", description: "The record id to update.", required: true }, data: { type: "object", description: "Fields to update.", required: false } } } }; paths[`/delete_${t}`] = { description: `Delete a record from the "${t}" entity by id.`, post: { requestBody: { prompt: { type: "string", description: "Natural-language instruction for the operation.", required: true }, id: { type: "string", description: "The record id to delete.", required: true } } } }; } return c.json({ title: `${siteName} MCP`, description: siteDesc, paths }); }); var handleAction = async (c, action) => { const denied = agenticGuard(c); if (denied) return denied; const clean = action.replace(/^\//, ""); const body = await c.req.json().catch(() => ({})); const listM = clean.match(/^list_(.+)$/); if (listM) { const entity = listM[1]; if (!await isValidEntity(c.env.DB, entity)) { return c.json({ success: false, error: `Unknown entity: ${entity}` }, 404); } const limit = Math.min(100, Number(body.limit) || 50); const page = Math.max(1, Number(body.page) || 1); const rows = await c.env.DB.prepare( `SELECT * FROM ${entity} ORDER BY created_at DESC LIMIT ? OFFSET ?` ).bind(limit, (page - 1) * limit).all().catch(() => ({ results: [] })); return c.json(mwResponse(clean, [`Fetched ${rows.results.length} records from "${entity}"`], { [entity]: rows.results, count: rows.results.length })); } const createM = clean.match(/^create_(.+)$/); if (createM) { const entity = createM[1]; if (!await isValidEntity(c.env.DB, entity)) { return c.json({ success: false, error: `Unknown entity: ${entity}` }, 404); } const record = { ...body.data || body }; delete record.prompt; if (!record.id) record.id = crypto.randomUUID(); if (!record.created_at) record.created_at = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19); const cols = Object.keys(record).filter((k) => /^[a-z_][a-z0-9_]*$/i.test(k)); if (cols.length) { const placeholders = cols.map(() => "?").join(", "); await c.env.DB.prepare( `INSERT INTO ${entity} (${cols.join(", ")}) VALUES (${placeholders})` ).bind(...cols.map((k) => record[k])).run().catch((e) => { throw e; }); } return c.json(mwResponse(clean, [`Injected new record into "${entity}"`], record)); } const updateM = clean.match(/^update_(.+)$/); if (updateM) { const entity = updateM[1]; if (!await isValidEntity(c.env.DB, entity)) { return c.json({ success: false, error: `Unknown entity: ${entity}` }, 404); } const id = body.id; if (!id) return c.json({ success: false, error: "id is required" }, 400); const data = { ...body.data || {} }; delete data.id; delete data.prompt; const cols = Object.keys(data).filter((k) => /^[a-z_][a-z0-9_]*$/i.test(k)); if (!cols.length) return c.json({ success: false, error: "No fields to update" }, 400); await c.env.DB.prepare( `UPDATE ${entity} SET ${cols.map((k) => `${k} = ?`).join(", ")} WHERE id = ?` ).bind(...cols.map((k) => data[k]), id).run().catch(() => { }); const updated = await c.env.DB.prepare(`SELECT * FROM ${entity} WHERE id = ?`).bind(id).first(); return c.json(mwResponse(clean, [`Updated record ${id} in "${entity}"`], updated)); } const deleteM = clean.match(/^delete_(.+)$/); if (deleteM) { const entity = deleteM[1]; if (!await isValidEntity(c.env.DB, entity)) { return c.json({ success: false, error: `Unknown entity: ${entity}` }, 404); } const id = body.id; if (!id) return c.json({ success: false, error: "id is required" }, 400); await c.env.DB.prepare(`DELETE FROM ${entity} WHERE id = ?`).bind(id).run().catch(() => { }); return c.json(mwResponse(clean, [`Deleted record ${id} from "${entity}"`], { id, status: "deleted" })); } return c.json(mwResponse(clean, [`Unknown action "${clean}"`], { status: "noop" })); }; agentic.post("/:action", (c) => handleAction(c, c.req.param("action"))); agentic.get("/:action", (c) => handleAction(c, c.req.param("action"))); // backend/src/index.ts var app = new Hono3(); app.use("*", cors({ origin: "*", allowHeaders: ["Content-Type", "Authorization", "X-MAuth-Token", "x-mworld-origin", "mAuthAgenticToken"], allowMethods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] })); app.route("/", agentic); app.route("/api", public_default); app.route("/api/admin", admin_default); var src_default = app; // unified-entry.ts var MIME_TYPES = { ".html": "text/html; charset=utf-8", ".js": "application/javascript; charset=utf-8", ".mjs": "application/javascript; charset=utf-8", ".css": "text/css; charset=utf-8", ".json": "application/json; charset=utf-8", ".svg": "image/svg+xml", ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".webp": "image/webp", ".gif": "image/gif", ".ico": "image/x-icon", ".woff": "font/woff", ".woff2": "font/woff2", ".ttf": "font/ttf", ".wasm": "application/wasm", ".txt": "text/plain; charset=utf-8" }; var unified_entry_default = { async fetch(request, env, ctx) { const url = new URL(request.url); const method = request.method; if (method === "OPTIONS") { return new Response(null, { status: 204, headers: { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, Authorization" } }); } if (url.pathname.startsWith("/api")) { return src_default.fetch(request, env, ctx); } const response = await env.ASSETS.fetch(request); const currentType = response.headers.get("content-type"); if (!currentType || currentType === "" || currentType === "application/octet-stream") { const pathname = url.pathname; const dotIdx = pathname.lastIndexOf("."); const ext = dotIdx !== -1 ? pathname.substring(dotIdx).toLowerCase() : ""; const detected = MIME_TYPES[ext] || (pathname.endsWith("/") || !ext ? "text/html; charset=utf-8" : null); if (detected) { const newHeaders = new Headers(response.headers); newHeaders.set("content-type", detected); return new Response(response.body, { status: response.status, statusText: response.statusText, headers: newHeaders }); } } return response; } }; export { unified_entry_default as default };