@usetoki/toki 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +177 -0
- package/dist/app.d.ts +137 -0
- package/dist/app.js +608 -0
- package/dist/app.js.map +1 -0
- package/dist/cookies.d.ts +17 -0
- package/dist/cookies.js +54 -0
- package/dist/cookies.js.map +1 -0
- package/dist/forms.d.ts +13 -0
- package/dist/forms.js +64 -0
- package/dist/forms.js.map +1 -0
- package/dist/group.d.ts +19 -0
- package/dist/group.js +51 -0
- package/dist/group.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/inject.d.ts +19 -0
- package/dist/inject.js +56 -0
- package/dist/inject.js.map +1 -0
- package/dist/jwt.d.ts +57 -0
- package/dist/jwt.js +128 -0
- package/dist/jwt.js.map +1 -0
- package/dist/logger.d.ts +7 -0
- package/dist/logger.js +45 -0
- package/dist/logger.js.map +1 -0
- package/dist/middleware.d.ts +38 -0
- package/dist/middleware.js +133 -0
- package/dist/middleware.js.map +1 -0
- package/dist/native.d.ts +81 -0
- package/dist/native.js +38 -0
- package/dist/native.js.map +1 -0
- package/dist/pipeline.d.ts +16 -0
- package/dist/pipeline.js +125 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/request.d.ts +65 -0
- package/dist/request.js +170 -0
- package/dist/request.js.map +1 -0
- package/dist/response.d.ts +33 -0
- package/dist/response.js +92 -0
- package/dist/response.js.map +1 -0
- package/dist/schema.d.ts +45 -0
- package/dist/schema.js +179 -0
- package/dist/schema.js.map +1 -0
- package/dist/static.d.ts +20 -0
- package/dist/static.js +105 -0
- package/dist/static.js.map +1 -0
- package/dist/types.d.ts +88 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Toki contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/toki-logo.png" alt="Toki" width="280">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<strong>A blazing-fast HTTP framework for Node.js.</strong> ⚡
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
A clean, fully-typed TypeScript API on top of a native HTTP engine written in
|
|
11
|
+
<strong>Zig</strong>. Parsing, routing, static files, and compression run in native
|
|
12
|
+
code; your handlers stay in JavaScript.
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
The engine runs on Node's **own libuv loop** and calls handlers **synchronously** on
|
|
16
|
+
the JS thread — no worker threads, no `ThreadsafeFunction`, no cross-thread hop. On
|
|
17
|
+
the plaintext benchmark it sustains ~99k req/s at ~49 MB RSS on a single thread.
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { createApp, reply } from "@usetoki/toki";
|
|
21
|
+
|
|
22
|
+
const app = createApp();
|
|
23
|
+
|
|
24
|
+
app.get("/", () => reply.text("Hello, World!"));
|
|
25
|
+
app.get("/users/:id", (req) => reply.json({ id: req.params.id }));
|
|
26
|
+
|
|
27
|
+
app.listen(3000);
|
|
28
|
+
console.log("listening on http://127.0.0.1:3000");
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## ✨ Why Toki
|
|
32
|
+
|
|
33
|
+
- ⚡ **Native engine** — HTTP/1.1 parsing, routing, and I/O run in Zig, on Node's own loop.
|
|
34
|
+
- 🪶 **Tiny footprint** — a single-thread server in ~49 MB, ~2× leaner than `node:http`.
|
|
35
|
+
- 🧩 **Fully typed** — strict TypeScript, no `any`, real editor autocompletion.
|
|
36
|
+
- 🔌 **Batteries included** — routing, hooks, middleware, route groups, plugins, cookies, CORS, security headers, logging, `req.id` / `req.ip`.
|
|
37
|
+
- 📦 **Body parsing** — JSON, urlencoded, and `multipart/form-data` uploads, plus pluggable content-type parsers.
|
|
38
|
+
- 🗂️ **Static files** — MIME, `ETag` / `304`, `HEAD`, traversal-safe, with pre-computed gzip/brotli.
|
|
39
|
+
- 🗜️ **Compression** — gzip + brotli, negotiated per `Accept-Encoding`, off the event loop.
|
|
40
|
+
- 🌊 **Streaming** — `reply.stream` over chunked transfer encoding, with native backpressure.
|
|
41
|
+
- 🛡️ **Hardened** — schema validation, JWT, a native per-IP rate limiter, slowloris guard, configurable limits.
|
|
42
|
+
- 🧪 **Testable** — `app.inject()` runs a real request in-process, no port needed.
|
|
43
|
+
|
|
44
|
+
## 🚀 Install
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install @usetoki/toki
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or build from source — see [Build from source](#-build-from-source). You'll need
|
|
51
|
+
[Zig](https://ziglang.org) 0.16.0 and Node.js 20+.
|
|
52
|
+
|
|
53
|
+
## 📖 Features at a glance
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { createApp, reply, cors, securityHeaders, compression, jwtAuth } from "@usetoki/toki";
|
|
57
|
+
|
|
58
|
+
const app = createApp({ logger: "info" });
|
|
59
|
+
|
|
60
|
+
// Middleware + the full hook pipeline
|
|
61
|
+
app.use(cors({ origin: ["https://app.example"] }));
|
|
62
|
+
app.use(securityHeaders({ hsts: true }));
|
|
63
|
+
app.addHook("onResponse", compression());
|
|
64
|
+
|
|
65
|
+
// Route groups with a shared prefix + scoped hooks
|
|
66
|
+
app.group("/api/v1", (api) => {
|
|
67
|
+
api.use(jwtAuth({ secret: process.env.JWT_SECRET! }));
|
|
68
|
+
api.get("/me", (req) => reply.json(req.user));
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Encapsulated plugins
|
|
72
|
+
app.register(async (instance) => {
|
|
73
|
+
instance.get("/health", () => ({ status: "ok" }));
|
|
74
|
+
}, { prefix: "/internal" });
|
|
75
|
+
|
|
76
|
+
// Schema validation (custom messages) + response serialization
|
|
77
|
+
app.post("/users", {
|
|
78
|
+
schema: {
|
|
79
|
+
body: {
|
|
80
|
+
type: "object",
|
|
81
|
+
required: ["name"],
|
|
82
|
+
properties: { name: { type: "string", minLength: 2 } },
|
|
83
|
+
errorMessage: { required: { name: "name is required" } },
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
}, (req) => reply.json({ created: req.json<{ name: string }>().name }, 201));
|
|
87
|
+
|
|
88
|
+
// Uploads (req.form), static files, streaming, rate limiting
|
|
89
|
+
app.post("/upload", (req) => reply.json({ files: req.form?.files.length ?? 0 }));
|
|
90
|
+
app.static("/assets", "./public");
|
|
91
|
+
app.get("/events", () => reply.stream(sse(), { contentType: "text/event-stream" }));
|
|
92
|
+
|
|
93
|
+
app.listen(3000, { rateLimit: { max: 100, windowMs: 60_000 } });
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Request:** `req.method`, `req.path`, `req.params`, `req.query`, `req.headers`,
|
|
97
|
+
`req.cookies`, `req.body`, `req.form`, `req.ip`, `req.hostname`, `req.protocol`,
|
|
98
|
+
`req.id`, `req.log`, `req.text()`, `req.json<T>()`, `await req.parseBody()`.
|
|
99
|
+
|
|
100
|
+
**Reply:** `reply.text / html / json / empty / redirect / bytes / stream`.
|
|
101
|
+
|
|
102
|
+
**Built in:** `cors()`, `securityHeaders()`, `compression()`, `signJwt` / `verifyJwt`
|
|
103
|
+
/ `jwtAuth`, `setErrorHandler`, `setNotFoundHandler`, `addContentTypeParser`.
|
|
104
|
+
|
|
105
|
+
## ⚙️ `listen` options
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
app.listen(3000, { host: "0.0.0.0", maxBodyBytes: 5_000_000 });
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
| Option | Default | Description |
|
|
112
|
+
| --- | --- | --- |
|
|
113
|
+
| `host` | `0.0.0.0` | Bind interface. Pass `0` as the port to pick a free one. |
|
|
114
|
+
| `maxBodyBytes` | 1 MiB | Largest accepted request body (`413` above it). |
|
|
115
|
+
| `maxHeaders` | 128 | Max header lines per request. |
|
|
116
|
+
| `backlog` | 512 | Listen backlog. |
|
|
117
|
+
| `headerTimeoutMs` | 0 | Close a connection stalled mid-request, in ms; `0` disables (slowloris guard). |
|
|
118
|
+
| `reusePort` | `false` | `SO_REUSEPORT` for kernel-balanced multi-worker scaling (Linux/BSD). |
|
|
119
|
+
| `rateLimit` | — | `{ max, windowMs }` — native per-IP limiter; over-limit requests get a `429` before reaching JS. |
|
|
120
|
+
|
|
121
|
+
`createApp({ logger, requestTimeoutMs })` configures the app; `app.listen` returns a
|
|
122
|
+
handle whose `close()` shuts the server down gracefully.
|
|
123
|
+
|
|
124
|
+
## 🧭 Native vs JavaScript — the boundary
|
|
125
|
+
|
|
126
|
+
The shared, heavy logic is native: HTTP parsing, routing, the MIME table, ETag and
|
|
127
|
+
header assembly, status phrases, static serving, and compression negotiation. The
|
|
128
|
+
TypeScript layer is the developer API plus the unavoidable Node bits (the handler
|
|
129
|
+
pipeline, `fs`/`zlib` for static assets).
|
|
130
|
+
|
|
131
|
+
That line is drawn on purpose, and it's measured. Building a parsed request object in
|
|
132
|
+
Zig and handing it to V8 means ~20 N-API calls per request — slower than letting V8's
|
|
133
|
+
own C++ `URLSearchParams` / `Headers` / `JSON` do it. So query/header/cookie/JSON
|
|
134
|
+
parsing and schema validation stay in TypeScript: crossing the N-API boundary to
|
|
135
|
+
"go native" there would make it slower, which is the opposite of the point.
|
|
136
|
+
|
|
137
|
+
## 🔧 Build from source
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npm install
|
|
141
|
+
npm run build # native addon (ReleaseFast) + TypeScript → dist/
|
|
142
|
+
npm test # zig build test + the Node suite
|
|
143
|
+
npm run lint # zig fmt + tsc + prettier
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`npm run build:debug` is a faster, unoptimized native build for iteration. The build
|
|
147
|
+
cross-compiles: `zig build -Dtarget=aarch64-linux-gnu` (and friends) produces the
|
|
148
|
+
addon for any platform from one host.
|
|
149
|
+
|
|
150
|
+
## 📂 Layout
|
|
151
|
+
|
|
152
|
+
| Folder | What |
|
|
153
|
+
| --- | --- |
|
|
154
|
+
| `src/` | Zig engine (Node-API addon) — parser, router, response, static, streaming, rate limiter. |
|
|
155
|
+
| `ts/` | TypeScript framework layer → `dist/`. |
|
|
156
|
+
| `__test__/` | Tests (`node:test`, run as `.ts`). |
|
|
157
|
+
| `examples/` | A runnable, self-checking example per feature. |
|
|
158
|
+
|
|
159
|
+
## 🧪 Examples
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
node examples/routing.ts
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Browse [`examples/`](./examples) for routing, async handlers, hooks, groups, plugins,
|
|
166
|
+
validation, cookies, static files, forms, CORS, compression, rate limiting, JWT,
|
|
167
|
+
streaming, and graceful shutdown.
|
|
168
|
+
|
|
169
|
+
## Scope
|
|
170
|
+
|
|
171
|
+
Toki speaks HTTP/1.1. TLS and HTTP/2 are intentionally out of scope — terminate them
|
|
172
|
+
at a reverse proxy (nginx, Caddy), the standard production setup for Node. WebSockets
|
|
173
|
+
are not included.
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
MIT — see [LICENSE](./LICENSE).
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { RouteGroup } from "./group.js";
|
|
2
|
+
import { type InjectOptions, type InjectResponse } from "./inject.js";
|
|
3
|
+
import { type CorsOptions } from "./middleware.js";
|
|
4
|
+
import { type ServerOptions } from "./native.js";
|
|
5
|
+
import { type StaticOptions } from "./static.js";
|
|
6
|
+
import type { BodyParser, ContentTypeParserEntry, ErrorHandler, Handler, LifecycleHook, ListenHandle, Logger, Middleware, PluginOptions, ResponseHook, RouteMethod, RouteOptions, SerializationHook, TimeoutHook, TokiOptions } from "./types.js";
|
|
7
|
+
interface Route {
|
|
8
|
+
readonly method: RouteMethod;
|
|
9
|
+
readonly path: string;
|
|
10
|
+
readonly handler: Handler;
|
|
11
|
+
readonly options: RouteOptions;
|
|
12
|
+
readonly groupMiddleware: readonly Middleware[];
|
|
13
|
+
readonly scope: Scope;
|
|
14
|
+
}
|
|
15
|
+
interface PluginEntry {
|
|
16
|
+
readonly plugin: TokiPlugin;
|
|
17
|
+
readonly opts: PluginOptions;
|
|
18
|
+
readonly scope: Scope;
|
|
19
|
+
}
|
|
20
|
+
/** Registers routes/hooks/decorators on the encapsulated `instance`. */
|
|
21
|
+
export type TokiPlugin = (instance: TokiInstance, opts: PluginOptions) => void | Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Encapsulated registration scope: own hooks, middleware, request decorators,
|
|
24
|
+
* error handler, and path prefix. Routes here run the hook chains of this scope
|
|
25
|
+
* and all ancestors. The root app ({@link Toki}) is itself a scope; {@link
|
|
26
|
+
* Scope.register} creates children.
|
|
27
|
+
*/
|
|
28
|
+
export declare class Scope {
|
|
29
|
+
#private;
|
|
30
|
+
readonly parent: Scope | undefined;
|
|
31
|
+
readonly prefix: string;
|
|
32
|
+
readonly onRequest: Middleware[];
|
|
33
|
+
readonly preParsing: Middleware[];
|
|
34
|
+
readonly middleware: Middleware[];
|
|
35
|
+
readonly preValidation: Middleware[];
|
|
36
|
+
readonly preHandler: Middleware[];
|
|
37
|
+
readonly preSerialization: SerializationHook[];
|
|
38
|
+
readonly onResponse: ResponseHook[];
|
|
39
|
+
readonly onSend: ResponseHook[];
|
|
40
|
+
readonly onTimeout: TimeoutHook[];
|
|
41
|
+
readonly requestDecorators: Record<string, unknown>;
|
|
42
|
+
readonly contentTypeParsers: ContentTypeParserEntry[];
|
|
43
|
+
readonly plugins: PluginEntry[];
|
|
44
|
+
errorHandler?: ErrorHandler;
|
|
45
|
+
constructor(parent: Scope | undefined, prefix: string);
|
|
46
|
+
get root(): Toki;
|
|
47
|
+
get log(): Logger;
|
|
48
|
+
get(path: string, handler: Handler): this;
|
|
49
|
+
get(path: string, options: RouteOptions, handler: Handler): this;
|
|
50
|
+
post(path: string, handler: Handler): this;
|
|
51
|
+
post(path: string, options: RouteOptions, handler: Handler): this;
|
|
52
|
+
put(path: string, handler: Handler): this;
|
|
53
|
+
put(path: string, options: RouteOptions, handler: Handler): this;
|
|
54
|
+
patch(path: string, handler: Handler): this;
|
|
55
|
+
patch(path: string, options: RouteOptions, handler: Handler): this;
|
|
56
|
+
delete(path: string, handler: Handler): this;
|
|
57
|
+
delete(path: string, options: RouteOptions, handler: Handler): this;
|
|
58
|
+
head(path: string, handler: Handler): this;
|
|
59
|
+
options(path: string, handler: Handler): this;
|
|
60
|
+
route(method: RouteMethod, path: string, handler: Handler): this;
|
|
61
|
+
/** Add middleware; runs before every handler in this scope and descendants. */
|
|
62
|
+
use(middleware: Middleware): this;
|
|
63
|
+
/** Register a lifecycle hook scoped to this instance. */
|
|
64
|
+
addHook(name: "onRequest" | "preParsing" | "preValidation" | "preHandler", fn: Middleware): this;
|
|
65
|
+
addHook(name: "preSerialization", fn: SerializationHook): this;
|
|
66
|
+
addHook(name: "onResponse" | "onSend", fn: ResponseHook): this;
|
|
67
|
+
addHook(name: "onTimeout", fn: TimeoutHook): this;
|
|
68
|
+
/** Set the error handler for routes in this scope (alias: `onError`). */
|
|
69
|
+
setErrorHandler(handler: ErrorHandler): this;
|
|
70
|
+
onError(handler: ErrorHandler): this;
|
|
71
|
+
/** Attach a property to every request handled in this scope. */
|
|
72
|
+
decorateRequest(name: string, value: unknown): this;
|
|
73
|
+
/**
|
|
74
|
+
* Register a body parser for one or more content types, consulted by
|
|
75
|
+
* `req.parseBody()`. `type` matches case-insensitively as a prefix (e.g.
|
|
76
|
+
* `"application/xml"`), a `RegExp`, or `"*"` for any. A child scope overrides
|
|
77
|
+
* an ancestor for the same type.
|
|
78
|
+
*/
|
|
79
|
+
addContentTypeParser(type: string | string[] | RegExp, parser: BodyParser): this;
|
|
80
|
+
/** Attach a property to the application instance (app-global, not encapsulated). */
|
|
81
|
+
decorate(name: string, value: unknown): this;
|
|
82
|
+
/** Set the handler for unmatched routes (app-global). */
|
|
83
|
+
setNotFoundHandler(handler: Handler): this;
|
|
84
|
+
/** Enable CORS: stage headers on every response and answer preflight `OPTIONS`. */
|
|
85
|
+
cors(options?: CorsOptions): this;
|
|
86
|
+
/** Serve files under `dir` at `urlPrefix` (joined with this scope's prefix). */
|
|
87
|
+
static(urlPrefix: string, dir: string, options?: StaticOptions): this;
|
|
88
|
+
/** Register a prefixed group of routes with its own middleware. */
|
|
89
|
+
group(prefix: string, build: (group: RouteGroup) => void): this;
|
|
90
|
+
/**
|
|
91
|
+
* Register a plugin into a fresh child scope. The plugin receives that scope as
|
|
92
|
+
* its `instance`; routes/hooks/decorators it adds are encapsulated there (and
|
|
93
|
+
* inherited by its own children), not leaked to the parent. Async plugins load
|
|
94
|
+
* during {@link Toki.ready}. `opts.prefix` mounts the plugin's routes under a path.
|
|
95
|
+
*/
|
|
96
|
+
register(plugin: TokiPlugin, opts?: PluginOptions): this;
|
|
97
|
+
}
|
|
98
|
+
/** The registration surface a plugin receives (the application or a child scope). */
|
|
99
|
+
export type TokiInstance = Scope;
|
|
100
|
+
/**
|
|
101
|
+
* The application. Register routes (optionally with a validation schema and
|
|
102
|
+
* route-scoped hooks), global hooks, middleware, and plugins, then
|
|
103
|
+
* {@link Toki.listen}. The pipeline stays synchronous until a step returns a
|
|
104
|
+
* `Promise`, keeping the common sync request on the fast path.
|
|
105
|
+
*/
|
|
106
|
+
export declare class Toki extends Scope {
|
|
107
|
+
#private;
|
|
108
|
+
constructor(options?: TokiOptions);
|
|
109
|
+
/** @internal */
|
|
110
|
+
get _logger(): Logger;
|
|
111
|
+
/** @internal */
|
|
112
|
+
_pushRoute(route: Route): void;
|
|
113
|
+
/** @internal */
|
|
114
|
+
_decorate(name: string, value: unknown): void;
|
|
115
|
+
/** @internal */
|
|
116
|
+
_setNotFound(handler: Handler): void;
|
|
117
|
+
/** @internal */
|
|
118
|
+
_mountStatic(prefix: string, dir: string, options?: StaticOptions): void;
|
|
119
|
+
/** Run `fn` once at {@link Toki.listen}, before serving. */
|
|
120
|
+
onReady(fn: LifecycleHook): this;
|
|
121
|
+
/** Run `fn` when the server closes. */
|
|
122
|
+
onClose(fn: LifecycleHook): this;
|
|
123
|
+
/** Load all registered plugins, awaiting async ones. Call before {@link Toki.listen}
|
|
124
|
+
* when any plugin is async; `listen` loads sync plugins on its own. */
|
|
125
|
+
ready(): Promise<void>;
|
|
126
|
+
/** Bind `port` and serve. Returns a handle whose `close()` stops the server. */
|
|
127
|
+
listen(port: number, options?: ServerOptions): ListenHandle;
|
|
128
|
+
/**
|
|
129
|
+
* Inject a request in-process for testing. Sends a real request over loopback so
|
|
130
|
+
* the full native path runs, auto-binding an ephemeral port when not already
|
|
131
|
+
* listening. Returns the captured response.
|
|
132
|
+
*/
|
|
133
|
+
inject(options: InjectOptions | string): Promise<InjectResponse>;
|
|
134
|
+
}
|
|
135
|
+
/** Create a new application. */
|
|
136
|
+
export declare function createApp(options?: TokiOptions): Toki;
|
|
137
|
+
export {};
|