TomoeJS — The art of perfect balance.
Ultra-Fast · Contract-Driven · Compile-Time Type Safety · Zero-Dependency Web Standards Core
Tomoe (巴) is a next-generation, high-performance web framework designed with a single goal: bridging the gap between strict backend correctness and supreme developer experience.
Inspired by the design of frameworks like Hono and Elysia, Tomoe embraces native Web Standard APIs (Request, Response, Headers, Cookies) making it universally portable across Bun, Cloudflare Workers, Node.js, Deno, and Vercel.
However, Tomoe addresses the single largest flaw in modern web frameworks: unsafe, untyped middleware side-effects.
Tomoe stands out from other modern frameworks by introducing architectural innovations that yield extreme performance and absolute correctness:
- ⚡ Zero-Wrapper Native Execution: Unlike other frameworks that wrap every incoming HTTP request inside custom classes (adding memory allocation and garbage collection pressure), Tomoe processes native WHATWG
RequestandResponseobjects directly. This yields near-zero overhead and lets you run at native C++ runtime speeds. - 🛡️ Precondition Validation: Relics and guards compile into a dependency graph at startup (
app.compile()). If you have a misconfigured or missing dependency, Tomoe throws an error at startup, completely preventing runtime crashes or forgotten authentication bugs from ever reaching production. - 🧅 Pre-Compiled Onion Pipeline: Standard Koa/Hono frameworks dynamically filter and search the middleware stack on every single request. Tomoe pre-computes and compiles the middleware execution stack for every single route at startup. When a request matches in the Radix tree, it invokes the optimized runner immediately, completely eliminating runtime middleware search overhead.
- 📉 V8-Optimized Functional Errors: Standard exception throwing (
throw new Error(...)) is one of the most expensive operations in V8, as it gathers full CPU call stack traces. Tomoe introduces a functional error signaling system (err(HttpError)/isErr(result)). This bypasses the expensive stack trace collection entirely, speeding up failure-path request responses (like validation or auth failures) by up to 10x.
To ensure absolute honesty and fairness, we run scientific load tests using Autocannon at 100 concurrent connections in a fully isolated sandbox. Every server process and socket port is programmatically cleaned and cleared before each run to prevent port sharing or CPU starvation.
- Operating System: Windows 11 (emulating production runtime loads)
- Node version:
v24.13.0 - Bun version:
v1.3.3(or equivalent local version) - Framework Versions:
- TomoeJS:
v1.0.0-rc.1 - Hono:
v4.12.22(Latest stable release) - Elysia:
v1.4.28(Latest stable release) - Express:
v5.2.1(Latest major Express 5)
- TomoeJS:
Measures baseline parsing, response writing dispatch, and simple static routing.
| Framework | Requests / Sec (Throughput) | Avg Latency (ms) | P99 Latency (ms) |
|---|---|---|---|
| Hono (Bun) | 43,152 req/s | 1.71 ms | 4 ms |
| Elysia (Bun) | 39,798 req/s | 2.00 ms | 6 ms |
| 👑 TomoeJS (Bun) | 36,789 req/s | 2.19 ms | 7 ms |
| Hono (Node) | 30,486 req/s | 2.81 ms | 7 ms |
| Express (Node) | 19,671 req/s | 4.59 ms | 9 ms |
| TomoeJS (Node) | 11,433 req/s | 8.25 ms | 22 ms |
Note
Honest Comparison: In a pure serialization test, Hono (Bun) and Elysia (Bun) utilize their custom low-level C++ request handlers in JSC to deliver outstanding throughput. TomoeJS runs exceptionally fast and neck-and-neck, serving 36,789 req/s natively on Bun.serve.
Tests parameter extraction speed, rad tree traversal, and URL path segment decoding.
| Framework | Requests / Sec (Throughput) | Avg Latency (ms) | P99 Latency (ms) |
|---|---|---|---|
| 👑 TomoeJS (Bun) | 39,952 req/s | 2.06 ms | 4 ms |
| Elysia (Bun) | 39,299 req/s | 2.10 ms | 4 ms |
| Hono (Bun) | 35,870 req/s | 2.29 ms | 7 ms |
| Hono (Node) | 31,566 req/s | 2.68 ms | 5 ms |
| Express (Node) | 19,906 req/s | 4.53 ms | 9 ms |
| TomoeJS (Node) | 11,566 req/s | 8.13 ms | 14 ms |
Note
Correctness meets Speed: TomoeJS is the fastest framework on dynamic routing, beating Elysia and Hono on Bun! Our backtracking radix path algorithm is extremely fast, decoding parameter segments natively without RegExp matching overhead.
Tests real-world middleware execution under composition (3 sequential middlewares checking CORS headers, Trace IDs, and Bearer Auth credentials).
| Framework | Requests / Sec (Throughput) | Avg Latency (ms) | P99 Latency (ms) |
|---|---|---|---|
| 👑 TomoeJS (Bun) | 37,565 req/s | 2.20 ms | 5 ms |
| Elysia (Bun) | 37,469 req/s | 2.28 ms | 4 ms |
| Hono (Bun) | 28,579 req/s | 3.00 ms | 8 ms |
| Hono (Node) | 24,443 req/s | 3.59 ms | 6 ms |
| Express (Node) | 17,503 req/s | 5.21 ms | 9 ms |
| TomoeJS (Node) | 11,930 req/s | 7.90 ms | 14 ms |
Note
Pre-compiled for real-world load: TomoeJS is 30% faster than Hono (Bun) under middleware composition! While Hono and Express search and bind middleware arrays dynamically on every incoming request, TomoeJS pre-computes and compiles route-level middleware execution lists at startup, saving massive execution cycles.
In traditional frameworks (Express, Fastify, Hono, Elysia), middlewares inject state into your request context behind the scenes (e.g. req.user, c.set("user")).
This creates a fragile runtime contract:
// ❌ Dangerous: 'user' might be undefined if you forget the middleware or register it in the wrong order!
app.use(authMiddleware)
app.get("/me", (c) => c.json(c.get("user"))) Tomoe removes these assumptions entirely by introducing Relics & Guards (Contract-Driven Architecture). If a route handler depends on something (like a verified database user), that precondition must be explicitly declared as a contract. If a contract isn't satisfied at startup, your application fails immediately rather than throwing runtime errors in production.
// Context is fully typed, and ctx.user is guaranteed to exist at compile time and runtime!
app.get("/me", authRelic, (ctx) => {
return ctx.json(ctx.user)
})- 🚀 Universal Web Standard Core: Zero dependencies, running natively on standard
RequestandResponseinterfaces. - ⚡ Backtracking Radix Router: Dynamic segment routing (
:paramName), wildcards (*), static-route fast paths, and automatic URL parameter segment decoding. - 🛡️ Contract-Driven Type Safety: Declare requirements (Relics) and preconditions (Guards) at startup.
- 📦 Standard Schema Validation: Built-in, high-performance validation (
body,query,params,headers) supporting any standard validator schema (Zod, Valibot, ArkType, etc.) and TypeBox. - 🍪 Robust Cookie API: Lazy request cookie parsing cache and RFC 6265 cookie name validation shielding against injection attacks.
- 🛡️ Production-Ready Middlewares: Built-in OOM-proof Rate Limiter, Reverse-Proxy friendly Host-matching CSRF middleware, CORS, and formatted console Logger.
- 📝 Auto-Generated OpenAPI & Swagger UI: Serves interactive, self-documenting
/docswith locked Swagger UI versions (@5.18.2) and CORS secure links. - 🔌 E2E Path-Based Client SDK: Enjoy complete static type-safety across frontend and backend.
Initialize your project and install tomoejs using Bun or your package manager of choice:
# Using Bun (Recommended)
mkdir tomoe-app
bun init
bun add tomoejs@rc
# Using npm / pnpm / yarn
npm install tomoejs@rcBuild an ultra-fast REST API in seconds:
import { Tomoe } from "tomoejs"
const app = new Tomoe()
// Basic routing
app.get("/", (c) => c.text("Welcome to TomoeJS!"))
// Path parameters (automatically decoded)
app.get("/hello/:name", (c) => {
const name = c.param("name") // e.g. "Saif Rehman" from "/hello/Saif%20Rehman"
return c.json({ message: `Hello, ${name}!` })
})
export default appLaunch with Bun:
bun run index.tsTo see a complete, fully featured blueprint built with TomoeJS, check out our Anime API Example in the apps/examples directory.
It demonstrates a comprehensive, real-world setup covering:
- 🛡️ Guards & Relics: Injection of shared database contexts (
dbRelic) and API authorization guards (apiKeyGuard) composed dynamically viaunite(...). - 📦 Zod Input Schema Validation: Auto-validating and typing request bodies (
relic.body) and query limits (relic.query) at the route gateway. - 🧅 Scope-level Custom Error Handlers: Intercepting 401 and 403 errors originating from guards or handlers to output beautifully formatted, custom JSON error payloads.
- 📝 Interactive Swagger UI Docs: Serving auto-generated OpenAPI schemas on
/swagger.jsonand interactive Swagger panels on/docs.
To start the example on your machine:
cd apps/examples
# Run natively on Bun (Recommended)
pnpm start
# Run on standard Node.js
pnpm run start:nodeTomoe implements an optimized, backtracking Radix-Tree router. Routes can contain static paths, named parameters, and wildcard segments.
const app = new Tomoe()
// 1. Static route
app.get("/api/v1/status", (c) => c.json({ ok: true }))
// 2. Named path parameters (decoded automatically)
app.get("/api/users/:userId/posts/:postId", (ctx) => {
const { userId, postId } = ctx.params
return ctx.json({ userId, postId })
})
// 3. Wildcard routes
app.get("/static/*", (ctx) => {
const filepath = ctx.param("*")
return ctx.text(`Serving file: ${filepath}`)
})Tomoe provides two clean models for structuring large scale applications:
Split your endpoints into clean standalone sub-routers and mount them seamlessly at specific prefixes.
import { Tomoe } from "tomoejs"
// Standalone sub-router
const animeRouter = new Tomoe()
animeRouter.get("/list", (c) => c.json(["FMA", "Hunter x Hunter"]))
animeRouter.get("/:id", (c) => c.json({ id: c.param("id") }))
const app = new Tomoe()
// Mount with prefix. Endpoints resolved at `/api/anime/list` and `/api/anime/:id`
app.route("/api/anime", animeRouter)Group routes under a path prefix protected by a specific Relic access policy. All nested routes automatically gain type-safe access to relic properties.
app.scope("/admin", adminAccessRelics, (r) => {
r.get("/dashboard", (ctx) => {
// Access fully typed and validated properties from the scope relics!
return ctx.json({ user: ctx.user, org: ctx.org })
})
})Unlike other frameworks that wrap request objects with custom classes, Tomoe keeps the Request object 100% native for maximum performance and zero-allocation overhead. You can parse incoming payloads using Web Standard APIs (without Relics) or Contract-Driven Schema Validation (with Relics).
Use native W3C Fetch stream-consumers directly on ctx.req. This is ideal when you don't need validation or want low-overhead streaming (e.g. file uploads):
app.post("/raw-payloads", async (ctx) => {
// 1. Parse JSON body manually
const jsonBody = await ctx.req.json()
// 2. Read raw string payload (useful for custom text/xml parsing)
const textBody = await ctx.req.text()
// 3. Parse standard url-encoded forms or multipart uploads
const formData = await ctx.req.formData()
const username = formData.get("username")
const avatar = formData.get("avatar") // File object
// 4. Consume raw binary array buffers or file blobs (stream-friendly)
const arrayBuffer = await ctx.req.arrayBuffer()
const blob = await ctx.req.blob()
return ctx.json({ ok: true })
})Mount schema relics to automatically parse, clean, and validate payloads at the gateway. Whenever ctx.body exists in your handler, it is guaranteed to be statically typed and valid:
import { relic } from "tomoejs"
import { z } from "zod"
const registerSchema = z.object({
username: z.string().min(3),
email: z.string().email(),
})
// Validation relics automatically inject 'ctx.body', 'ctx.query', 'ctx.params', or 'ctx.headers'
app.post("/register", relic.body(registerSchema), (ctx) => {
// ctx.body is fully typed, autocomplete-ready, and 100% safe!
const { username, email } = ctx.body
return ctx.json({ status: "success", username })
})- Zero Object Allocation: Wrapping native requests on every call consumes unnecessary memory and puts garbage collection pressure on high-throughput servers. Tomoe executes at native C++ runtime speeds.
- No Stream Locking: Native
Requestbodies can only be read once. If a framework automatically reads it internally to populate a globalctx.body, it locks the stream, blocking developers from forwarding raw file streams to storage buckets (like AWS S3 or Cloudflare R2). - Graph Caching: In Hono, wrappers cache parsed bodies because multiple middlewares run sequentially and might repeatedly read them. In Tomoe, relics compile into a dependency graph at startup. If multiple handlers or guards need the body,
relic.body()resolves exactly once, caches it in the Relic store, and injects it—preventing locking without custom wrapper overhead.
Handlers receive a fully typed Context object wrapping the standard Request and providing clean response utilities.
ctx.req: StandardRequestobject.ctx.query(key): Retrieves a single URL query parameter.ctx.queries: Retrieves all URL query parameters as a key-value record.ctx.header(name): Case-insensitive retrieval of a request header.ctx.json(data, init?): Returns a JSON response with properapplication/jsonheaders.ctx.text(body, init?): Returns a text response with propertext/plainheaders.ctx.html(html, init?): Returns an HTML response withtext/htmlheaders.ctx.redirect(url, status?): Returns a redirection response (default:302).
Tomoe provides lazy-loaded cookie management utilities and robust security defenses:
app.get("/cookies", (ctx) => {
// 1. Read request cookie (lazy parsed and cached)
const token = ctx.cookie("session_token")
// 2. Set response cookies (RFC 6265 validated to prevent injection attacks)
ctx.setCookie("user", "saif", {
httpOnly: true,
secure: true,
sameSite: "Strict",
maxAge: 3600
})
return ctx.json({ token })
})Instead of relying on standard unsafe middleware side-effects, declare explicit dependency chains.
- Relics: Generate values and inject them into handler contexts.
- Guards: Assert security policies or validation checks (cannot inject variables).
import { relic, guard, err, Unauthorized, Forbidden } from "tomoejs"
// 1. Define Providing Relic (injects typed "user" value)
const auth = relic("user", async (ctx) => {
const authHeader = ctx.header("authorization")
if (!authHeader) return err(Unauthorized) // Return functional error
const user = await db.verify(authHeader)
if (!user) return err(Unauthorized)
return user
})
// 2. Define Guard (validates state using other relics)
const adminOnly = guard(async (ctx, use) => {
const user = use(auth) // Resolves auth relic dependency
if (!user.isAdmin) return err(Forbidden)
})
// 3. Mount single relics or group them together using unite()
import { unite } from "tomoejs"
const adminAccess = unite(auth, adminOnly)
app.scope("/admin", adminAccess, (router) => {
// 'ctx.user' is fully typed as a User object, and guaranteed to be present!
router.get("/dashboard", (ctx) => {
return ctx.json({ secret: "admin_panel", activeUser: ctx.user })
})
})Tomoe offers built-in schema validation relics supporting standard schema systems (Zod, Valibot, ArkType, TypeBox).
import { z } from "zod"
const createPostSchema = z.object({
title: z.string().min(3),
content: z.string()
})
const querySchema = z.object({
limit: z.coerce.number().default(10)
})
// Mount standard body and query validation relics
app.post("/posts", unite(relic.body(createPostSchema), relic.query(querySchema)), (ctx) => {
// ctx.body and ctx.query are fully typed and validated!
const { title, content } = ctx.body
const { limit } = ctx.query
return ctx.json({ status: "created", post: { title, content }, limit })
})In Tomoe, Middleware controls request flow (like CORS, logs, and trace headers) while Relics define state contracts.
A Tomoe middleware is a standard function with the signature (ctx: Context, next: () => Promise<Response>) => Response | Promise<Response>:
import { Tomoe, type Middleware } from "tomoejs"
// 1. Example: A simple custom request timing middleware
const timerMiddleware = (): Middleware => {
return async (ctx, next) => {
const start = performance.now()
// Execute subsequent middlewares and handler
const response = await next()
const duration = (performance.now() - start).toFixed(2)
response.headers.set("X-Response-Time", `${duration}ms`)
return response
}
}
// 2. Example: Short-circuiting custom IP whitelist middleware
const ipWhitelist = (allowedIps: string[]): Middleware => {
return async (ctx, next) => {
const ip = ctx.header("X-Real-IP") || "unknown"
if (!allowedIps.includes(ip)) {
// Short-circuit: return response immediately without calling next()!
return ctx.json({ error: "Access Denied: IP blocked" }, { status: 403 })
}
return next()
}
}
const app = new Tomoe()
// Register custom middlewares globally
app.use(timerMiddleware())
// Register custom middlewares scoped to paths
app.use("/api/*", ipWhitelist(["1.2.3.4"]))Tomoe packages core production middlewares designed for extreme efficiency and OOM security:
import { cors } from "tomoejs"
app.use(cors({
origin: ["https://example.com"],
methods: ["GET", "POST"],
credentials: true
}))import { logger } from "tomoejs"
app.use(logger())import { csrf } from "tomoejs"
// Automatically supports reverse-proxies (reading X-Forwarded-Host)
// Normalizes protocol schemas and port numbers before checking hostname.
app.use(csrf({
origin: ["https://trusted-domain.com"]
}))import { rateLimit } from "tomoejs"
// Capped at 10,000 active keys with lazy sweeps to prevent Out-Of-Memory exploits
app.use(rateLimit({
windowMs: 60 * 1000, // 1 minute window
max: 60, // 60 requests per window
}))Tomoe implements an extremely fast, zero-overhead pipeline that supports both functional returns (recommended to avoid expensive V8 stack traces) and standard exceptions.
Tomoe exports standard, type-safe error constants covering all common REST and HTTP status codes. When returned or thrown, the framework automatically serializes them into standard JSON responses (e.g. { error: "Unauthorized" } with a 401 status).
| Category | Constant | Status Code | Default Message |
|---|---|---|---|
| Client Errors (4xx) | BadRequest |
400 | "Bad Request" |
Unauthorized |
401 | "Unauthorized" |
|
PaymentRequired |
402 | "Payment Required" (Customizable) |
|
Forbidden |
403 | "Forbidden" |
|
NotFound |
404 | "Not Found" |
|
MethodNotAllowed |
405 | "Method Not Allowed" |
|
NotAcceptable |
406 | "Not Acceptable" |
|
RequestTimeout |
408 | "Request Timeout" |
|
Conflict |
409 | "Conflict" |
|
Gone |
410 | "Gone" |
|
PayloadTooLarge |
413 | "Payload Too Large" |
|
UnsupportedMediaType |
415 | "Unsupported Media Type" |
|
UnprocessableEntity |
422 | "Unprocessable Entity" |
|
TooManyRequests |
429 | "Too Many Requests" |
|
| Server Errors (5xx) | ServerError |
500 | "Internal Server Error" |
NotImplemented |
501 | "Not Implemented" |
|
BadGateway |
502 | "Bad Gateway" |
|
ServiceUnavailable |
503 | "Service Unavailable" |
|
GatewayTimeout |
504 | "Gateway Timeout" |
If the pre-built error constants don't cover your use case, you can define your own custom HTTP errors in three elegant ways:
Create standard lightweight errors with customizable message strings:
import { httpError } from "tomoejs"
// Define a custom status/message
const TeapotError = httpError(418, "I'm a teapot")For rich API responses (e.g. containing validation maps or specific error codes), instantiate HttpError directly. The third argument accepts a details object which gets automatically merged into the final JSON output:
import { HttpError, err } from "tomoejs"
app.post("/checkout", (ctx) => {
if (insufficientFunds) {
const error = new HttpError(402, "Payment Failed", {
reason: "INSUFFICIENT_FUNDS",
availableBalance: 12.50,
required: 49.99
})
// Responds automatically with HTTP 402 and the JSON structure:
// { "error": "Payment Failed", "reason": "INSUFFICIENT_FUNDS", "availableBalance": 12.5, "required": 49.99 }
return err(error)
}
})Inherit directly from the HttpError class to integrate with domain-driven workflows:
import { HttpError } from "tomoejs"
class DatabaseTimeout extends HttpError {
constructor(query: string) {
super(504, "Database connection timeout", { query, timestamp: Date.now() })
this.name = "DatabaseTimeout"
}
}You can return errors functionally via err(...) (which is highly optimized in V8 by bypassing stack trace collection) or throw them natively:
import { err, Unauthorized, NotFound } from "tomoejs"
// 1. Functional returns in Relics (No throw, extremely fast)
const authRelic = relic("user", async (ctx) => {
const token = ctx.header("Authorization")
if (!token) return err(Unauthorized) // Return functional error
return db.verify(token)
})
// 2. Functional returns in Handlers
app.get("/user/:id", (ctx) => {
const user = db.find(ctx.param("id"))
if (!user) return err(NotFound) // Instantly sends 404
return ctx.json(user)
})
// 3. Thrown exceptions inside Handlers (Tomoe catches these automatically)
app.get("/admin", (ctx) => {
if (!ctx.user.isAdmin) {
throw Forbidden // Auto-caught and converted to standard 403 response
}
return ctx.text("Welcome")
})You can intercept specific HTTP error statuses. When registered within a scope, they catch errors originating from both relics and handlers in that scope:
app.scope("/api", authRelic, (r) => {
// Overrides all 401 Unauthorized errors in this prefix
r.onError(401, (ctx) => {
return ctx.json({ status: "error", message: "Please sign in to proceed" }, { status: 401 })
})
r.get("/profile", (ctx) => ctx.json(ctx.user))
})
// Global fallback handler for uncaught runtime exceptions
app.onError((err, ctx) => {
console.error(err)
return ctx?.json({ error: "Fatal Internal Crash" }, { status: 500 })
?? new Response("Fatal Error", { status: 500 })
})Tomoe automatically builds Swagger UI endpoints with support for circular schemas and Locked CDN resources.
import { swagger } from "tomoejs"
const app = new Tomoe()
// Serve Swagger UI on `/docs` and JSON spec on `/swagger.json`
swagger(app, {
title: "Tomoe API Documentation",
version: "1.0.0",
path: "/docs", // Swagger UI HTML endpoint
specPath: "/swagger.json" // OpenAPI spec JSON endpoint
})Tomoe JS compiles the entire backtracking radix tree, validates Relic dependency contracts, and caches middleware onion runners at startup to deliver peak production speeds.
const app = new Tomoe()
// ... register routes
// 1. Explicitly compile at startup (highly recommended in production)
app.compile()
// 2. Inspect dependency graph programmatically
const graph = app.graph()
console.log(`Routes registered: ${graph.routes.length}`)
console.log(`Tree Max Depth: ${graph.stats.maxDepth}`)No, calling it is completely optional.
If you do not call app.compile() at startup, the native fetch getter will automatically detect this and trigger compilation behind the scenes on the very first HTTP request that hits the server.
- ⚡ Eliminate Cold-Start Latency: Serverless platforms (like Cloudflare Workers, AWS Lambda, or Vercel) incur process cold starts. Compiling at startup shifts the compilation time to process boot, ensuring the first HTTP request gets an immediate, pre-compiled response.
- 🛡️ Fail-Fast Safety: Since Tomoe validates all Relic and Guard dependency trees during compilation, manual startup compilation ensures that any invalid or broken contracts cause the server to crash instantly at launch rather than waiting for a request to trigger a runtime failure.
Connect your frontend directly with complete static type safety:
// server.ts
const app = new Tomoe()
.post("/posts", relic.body(createPostSchema), (ctx) => {
return ctx.json({ status: "success", id: "123" })
})
export type AppRouter = typeof app// client.ts
import { createClient } from "tomoejs"
import type { AppRouter } from "./server"
const client = createClient<AppRouter>("http://localhost:3000")
// Type-safe variables, requests, headers, and responses!
const { data, error } = await client("/posts").post({
body: { title: "Tomoe Guide", content: "..." }
})Tomoe runs anywhere that supports modern Web Standards natively or through standard adapters.
Run natively at supreme speeds on the Bun JavaScript runtime:
import { Tomoe } from "tomoejs"
const app = new Tomoe()
app.get("/", (c) => c.text("Running natively on Bun!"))
export default appLaunch the server:
bun run --hot index.tsExport the application fetch handler directly:
import { Tomoe } from "tomoejs"
const app = new Tomoe()
app.get("/", (c) => c.text("Running on Cloudflare Workers!"))
export default {
fetch: app.fetch
}Deno natively supports standard Request/Response handlers:
import { Tomoe } from "tomoejs"
const app = new Tomoe()
app.get("/", (c) => c.text("Running on Deno!"))
Deno.serve((req) => app.fetch(req))Deploy on standard legacy Node.js environments using the built-in createServer stream adapter:
import { Tomoe, createServer } from "tomoejs"
const app = new Tomoe()
app.get("/", (c) => c.text("Running on Node.js!"))
const server = createServer(app)
server.listen(3000, () => {
console.log("Listening on http://localhost:3000")
})The official repository for TomoeJS is located at https://github.com/Project-Tomoe/tomoe.
For issues, feature requests, and code contributions, feel free to open a Pull Request or create an Issue.
This project is licensed under the MIT License. Built with 🌸 and perfect balance.
