@elysiumjs/core 0.8.0 → 0.11.0

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.
@@ -0,0 +1,74 @@
1
+ import type { TObject, TSchema } from 'typebox';
2
+ import type { ModuleGraph } from './module-graph';
3
+ import { t } from 'elysia';
4
+ import { ElysiumError } from './errors';
5
+ /**
6
+ * A value that must not leak: it prints as `[secret]` in logs, `JSON.stringify`, string
7
+ * conversion and error messages. Read it with `reveal()`.
8
+ * @author Axel Nana <axel.nana@workbud.com>
9
+ * @template T The type of the value.
10
+ */
11
+ export declare class Secret<T = string> {
12
+ #private;
13
+ constructor(value: T);
14
+ /** The secret value. Pass it where it is used, never to a log. */
15
+ reveal(): T;
16
+ toString(): string;
17
+ toJSON(): string;
18
+ /**
19
+ * Marks an environment variable as a secret: once validated, it is read as a `Secret` from
20
+ * `Env.get()`.
21
+ * @param schema The schema of the value. Defaults to a string.
22
+ * @example
23
+ * ```ts
24
+ * @Application.register({ env: t.Object({ DATABASE_URL: Secret.schema() }) })
25
+ * ```
26
+ */
27
+ static schema<S extends TSchema = ReturnType<typeof t.String>>(schema?: S): S;
28
+ /** Whether a schema was made by {@link Secret.schema}. */
29
+ static isSchema(schema: unknown): boolean;
30
+ }
31
+ /**
32
+ * One missing or invalid environment variable.
33
+ * @author Axel Nana <axel.nana@workbud.com>
34
+ */
35
+ export type EnvironmentIssue = {
36
+ key: string;
37
+ message: string;
38
+ };
39
+ /**
40
+ * Thrown at boot when environment variables are missing or invalid. It lists every issue, and
41
+ * never the values.
42
+ * @author Axel Nana <axel.nana@workbud.com>
43
+ */
44
+ export declare class InvalidEnvironmentError extends ElysiumError {
45
+ readonly issues: EnvironmentIssue[];
46
+ constructor(issues: EnvironmentIssue[]);
47
+ }
48
+ /**
49
+ * Thrown when two schemas declare the same environment variable differently.
50
+ * @author Axel Nana <axel.nana@workbud.com>
51
+ */
52
+ export declare class ConflictingEnvironmentSchemaError extends ElysiumError {
53
+ readonly key: string;
54
+ readonly owners: [string, string];
55
+ constructor(key: string, owners: [string, string]);
56
+ }
57
+ /**
58
+ * Merges the environment schema of the app with those of the active modules (`envSchema`).
59
+ * @author Axel Nana <axel.nana@workbud.com>
60
+ * @param appSchema The `env` option of the application.
61
+ * @param graph The module graph, or `null` for the app schema alone.
62
+ * @throws {ConflictingEnvironmentSchemaError} When two schemas declare a key differently.
63
+ */
64
+ export declare const mergeEnvironmentSchemas: (appSchema: TSchema | undefined, graph: ModuleGraph | null) => TObject | undefined;
65
+ /**
66
+ * Validates environment variables against a schema: applies defaults, converts strings to the
67
+ * declared types, keeps only the declared keys and wraps secrets.
68
+ * @author Axel Nana <axel.nana@workbud.com>
69
+ * @param schema The merged schema.
70
+ * @param raw The variables from the runtime.
71
+ * @returns The validated variables.
72
+ * @throws {InvalidEnvironmentError} Listing every missing or invalid variable.
73
+ */
74
+ export declare const validateEnvironment: (schema: TObject, raw: Record<string, unknown>) => Record<string, unknown>;
package/dist/console.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { WriteStream } from 'node:tty';
1
+ import type { OutputLevel } from './runtime';
2
2
  /**
3
3
  * ANSI color codes and styles for console output.
4
4
  * @author Axel Nana <axel.nana@workbud.com>
@@ -18,46 +18,78 @@ export declare enum ConsoleFormat {
18
18
  ITALIC = 11,
19
19
  INVERSE = 12
20
20
  }
21
+ /**
22
+ * The kinds of messages the level shortcuts of {@link InteractsWithConsole} write.
23
+ * @author Axel Nana <axel.nana@workbud.com>
24
+ */
25
+ export type ConsoleTag = 'debug' | 'info' | 'success' | 'warning' | 'error';
26
+ /**
27
+ * The tag written before a message: its label and the styles of the label.
28
+ * @author Axel Nana <axel.nana@workbud.com>
29
+ */
30
+ export type ConsoleTagStyle = {
31
+ label: string;
32
+ styles: ConsoleFormat[];
33
+ };
21
34
  /**
22
35
  * A utility class for console IO with various formatting options.
23
36
  * @author Axel Nana <axel.nana@workbud.com>
24
37
  */
25
38
  export declare class InteractsWithConsole {
26
39
  protected static readonly SPACE_WIDTH = 60;
40
+ /**
41
+ * Changes the tags the level shortcuts write before their messages, for every console. A tag
42
+ * is a label, or a label and its styles; the tags left out keep their defaults (`[D]`, `[I]`,
43
+ * `[S]`, `[W]` and `[E]`, in gray, blue, green, yellow and red).
44
+ * @author Axel Nana <axel.nana@workbud.com>
45
+ * @example
46
+ * ```ts
47
+ * InteractsWithConsole.useTags({ info: '[INFO]', error: { label: '[ERR]', styles: [ConsoleFormat.RED, ConsoleFormat.BOLD] } });
48
+ * ```
49
+ */
50
+ static useTags(custom: Partial<Record<ConsoleTag, string | ConsoleTagStyle>>): void;
51
+ /** Restores the default tags. */
52
+ static resetTags(): void;
27
53
  /**
28
54
  * Write a message to the console.
29
55
  * @param message The message to write.
30
56
  * @param newLine Whether to add a newline at the end (default: `true`).
31
- * @param out The output stream to write to (default: `process.stdout`).
57
+ * @param out The stream to write to (default: `'stdout'`). The runtime decides how it is
58
+ * written: hosts without streams, such as Cloudflare Workers, write to the console instead,
59
+ * and so does the framework when no runtime is set yet.
60
+ * @param level The level of the message. A host that writes through the console uses the
61
+ * console method of the level, so that its logs keep the level.
32
62
  */
33
- write(message: string, newLine?: boolean, out?: WriteStream): void;
63
+ write(message: string, newLine?: boolean, out?: 'stdout' | 'stderr', level?: OutputLevel): void;
34
64
  /**
35
65
  * Write a new line to the console.
36
66
  */
37
67
  newLine(): void;
68
+ /** Writes a message after the tag of its kind, at the level and on the stream of that kind. */
69
+ protected tagged(tag: ConsoleTag, message: string): void;
38
70
  /**
39
- * Write an info message to the console (blue text).
71
+ * Write an info message to the console, after the `info` tag.
40
72
  * @param message The message to write.
41
73
  */
42
74
  info(message: string): void;
43
75
  /**
44
- * Write a success message to the console (green text).
76
+ * Write a success message to the console, after the `success` tag.
45
77
  * @param message The message to write.
46
78
  */
47
79
  success(message: string): void;
48
80
  /**
49
- * Write a warning message to the console (yellow text).
81
+ * Write a warning message to the console, after the `warning` tag.
50
82
  * @param message The message to write.
51
83
  */
52
84
  warning(message: string): void;
53
85
  /**
54
- * Write an error message to the console (red text).
86
+ * Write an error message to the standard error stream, after the `error` tag.
55
87
  * @param message The message to write.
56
88
  */
57
89
  error(message: string): void;
58
90
  /**
59
- * Write a debug message to the console (gray text).
60
- * Only shown if the DEBUG environment variable is set.
91
+ * Write a debug message to the console, after the `debug` tag. Only written when the `DEBUG`
92
+ * environment variable is set.
61
93
  * @param message The message to write.
62
94
  */
63
95
  debug(message: string): void;
@@ -129,6 +161,11 @@ export declare class InteractsWithConsole {
129
161
  * Clear the current line in the console.
130
162
  */
131
163
  clearLine(): void;
164
+ /**
165
+ * Gets the interactive terminal of the runtime.
166
+ * @throws {RuntimeNotSupportedError} When the runtime has no terminal.
167
+ */
168
+ private terminal;
132
169
  /**
133
170
  * Prompt the user for input.
134
171
  * @param question The question to ask.
@@ -1,13 +1,16 @@
1
1
  import type { Class } from 'type-fest';
2
2
  import type { ModuleNode } from './module-graph';
3
3
  import type { ServiceDefinition } from './service';
4
+ import type { ServiceToken } from './token';
4
5
  import { ServiceScope } from './service';
5
6
  /**
6
7
  * A service registered in the container.
7
8
  * @author Axel Nana <axel.nana@workbud.com>
8
9
  */
9
10
  export type ContainerEntry = {
10
- /** The name the service is registered under. */
11
+ /** The token the service is registered under: its name, or its class. */
12
+ token: ServiceToken;
13
+ /** A readable name for messages. Never used to look the service up. */
11
14
  name: string;
12
15
  /** The service's scope. */
13
16
  scope: ServiceScope;
@@ -37,8 +40,8 @@ export type ContainerEntry = {
37
40
  export type VisibilityResolver = {
38
41
  /** The modules whose exports `node` can see. */
39
42
  visibleOwners(node: ModuleNode): readonly ModuleNode[];
40
- /** The service names `owner` exports, or `true` when it exports all its services. */
41
- exportsOf(owner: ModuleNode): readonly string[] | true;
43
+ /** The service tokens `owner` exports, or `true` when it exports all its services. */
44
+ exportsOf(owner: ModuleNode): readonly ServiceToken[] | true;
42
45
  };
43
46
  /**
44
47
  * The scoped service container: a global scope plus one scope per module.
@@ -52,10 +55,10 @@ export declare namespace Container {
52
55
  * @param owner The declaring module, or `null` for a service registered outside any module.
53
56
  * @param def A service class or factory definition.
54
57
  * @param config The owning module's resolved config, handed to factory definitions.
55
- * @returns The name the service was registered under.
58
+ * @returns The token the service was registered under: its name, or its class.
56
59
  * @throws {DuplicateGlobalServiceError} If a global service with that name already exists.
57
60
  */
58
- function register(owner: ModuleNode | null, def: ServiceDefinition, config: unknown): string;
61
+ function register(owner: ModuleNode | null, def: ServiceDefinition, config: unknown): ServiceToken;
59
62
  /**
60
63
  * Adds a prebuilt entry. A global entry goes to the global scope, any other to its owner's scope.
61
64
  * @throws {DuplicateGlobalServiceError} If the name is already taken in the global scope.
@@ -66,7 +69,7 @@ export declare namespace Container {
66
69
  * any definition with one of these names, so lookups reach the global entry.
67
70
  * Pass `null` to clear.
68
71
  */
69
- function setOverrides(names: Iterable<string> | null): void;
72
+ function setOverrides(tokens: Iterable<ServiceToken> | null): void;
70
73
  /**
71
74
  * Sets the module graph's visibility rules. Without a resolver, a module only sees
72
75
  * its own services and the global ones.
@@ -81,7 +84,7 @@ export declare namespace Container {
81
84
  * Sets the lookup consulted when a name isn't found, to report services
82
85
  * that belong to modules skipped for the current environment.
83
86
  */
84
- function setSkippedLookup(lookup: ((name: string) => {
87
+ function setSkippedLookup(lookup: ((token: ServiceToken) => {
85
88
  module: string;
86
89
  environment: string;
87
90
  } | null) | null): void;
@@ -89,7 +92,7 @@ export declare namespace Container {
89
92
  * Finds a service visible from `from`: its own scope, then the exports of the modules
90
93
  * it can see, then the global scope. Without `from`, only the global scope is searched.
91
94
  */
92
- function lookup(name: string, from: ModuleNode | undefined): {
95
+ function lookup(service: ServiceToken, from: ModuleNode | undefined): {
93
96
  entry: ContainerEntry;
94
97
  owner: ModuleNode | null;
95
98
  } | null;
@@ -98,31 +101,90 @@ export declare namespace Container {
98
101
  * @returns The service, or `null` if none is visible.
99
102
  * @throws {ServiceNotReadyError} If the service or its module is still initialising.
100
103
  */
101
- function get(name: string): unknown;
104
+ function get(service: ServiceToken): unknown;
105
+ /**
106
+ * Gets a service of the global scope, ignoring the services of the current module context.
107
+ * @returns The service, or `null` if there is no global service with that name.
108
+ * @throws {ServiceNotReadyError} If the service or its module is still initialising.
109
+ */
110
+ function getGlobal(service: ServiceToken): unknown;
102
111
  /**
103
112
  * Like {@link get}, but awaits the `onInit` of the service and of its owning module.
104
113
  * @throws {ServiceNotFoundError | ServiceNotVisibleError | EnvironmentSkippedModuleError}
105
114
  */
106
- function resolve(name: string, requester: string): Promise<unknown>;
115
+ function resolve(service: ServiceToken, requester: string): Promise<unknown>;
116
+ /**
117
+ * Awaits everything `cls` injects, so that {@link make} can then build it synchronously:
118
+ * module initialisation, singletons and, inside a request scope, request-scoped services
119
+ * with an asynchronous `onInit`.
120
+ * @param cls The class about to be built from the current module context.
121
+ */
122
+ function prepareClass(cls: Class<unknown>): Promise<void>;
123
+ /**
124
+ * Awaits a service like {@link resolve}, but returns `null` when it is not visible from the
125
+ * current module context, like {@link get}. A factory service is built once, with its `onInit` done.
126
+ * @param service The service token.
127
+ */
128
+ function resolveVisible(service: ServiceToken): Promise<unknown>;
107
129
  /**
108
130
  * Builds `cls` with its injected dependencies, resolved from the current module context.
109
131
  * @throws {ServiceNotFoundError | ServiceNotVisibleError | EnvironmentSkippedModuleError}
110
132
  */
111
133
  function make<T>(cls: Class<T>): T;
112
- /** Whether the global scope has a service with that name. */
113
- const hasGlobal: (name: string) => boolean;
114
- /** The names registered in the global scope. */
134
+ /**
135
+ * Instantiates a class that is kept for the app lifetime, like {@link make}. A request-scoped
136
+ * dependency throws {@link ScopeMismatchError}, since the instance would keep the first
137
+ * request's copy.
138
+ * @param cls The class to instantiate.
139
+ */
140
+ function makeSingleton<T>(cls: Class<T>): T;
141
+ /** The modules that declare a service with that token in their own scope, in registration order. */
142
+ const ownersOf: (service: ServiceToken) => ModuleNode[];
143
+ /** Whether the global scope has a service with that token. */
144
+ const hasGlobal: (service: ServiceToken) => boolean;
145
+ /** The names registered in the global scope (services registered by class are left out). */
115
146
  const globalNames: () => string[];
116
147
  /** Removes a service from the global scope. */
117
- const removeGlobal: (name: string) => void;
148
+ const removeGlobal: (service: ServiceToken) => void;
149
+ /**
150
+ * Runs `fn` in a request scope: request-scoped services resolved inside it, including in the
151
+ * asynchronous work it starts, get one instance for the whole run.
152
+ *
153
+ * Without `key`, a run inside another request scope joins it. With `key` (such as the request
154
+ * object), every run with the same key shares one scope, so separate hooks of one request see
155
+ * the same instances.
156
+ * @param fn The work to run.
157
+ * @param key The object the scope belongs to.
158
+ */
159
+ const runInRequestScope: <T>(fn: () => T, key?: object) => T;
160
+ /**
161
+ * Ends the request scope of `key`: removes its store and calls `onDestroy` on its instances,
162
+ * the last created first, each in its owner's module context. Errors are logged, never thrown.
163
+ * Disposing a scope twice returns the first disposal, so that every caller, such as a host
164
+ * that keeps the invocation alive until it ends, waits for the `onDestroy` work. Disposing a
165
+ * scope that never existed does nothing.
166
+ * @param key The object the scope was started with.
167
+ */
168
+ const disposeRequestScope: (key: object) => Promise<void>;
169
+ /** Whether the current code runs in a request scope. */
170
+ const inRequestScope: () => boolean;
118
171
  /** Clears the global scope. */
119
172
  const clearGlobal: () => void;
120
173
  /** The live singletons, in construction order (those whose `onInit` failed are left out). Returns a snapshot. */
121
174
  const constructed: () => readonly {
175
+ token: ServiceToken;
122
176
  name: string;
123
177
  instance: unknown;
124
178
  owner: ModuleNode | null;
125
179
  }[];
180
+ /**
181
+ * Forgets the singletons whose `onInit` failed, so that the next resolution builds them
182
+ * again.
183
+ * @param nodes The modules of the application to retry: only their services, and the global
184
+ * services they own, are forgotten, so that two applications in one process do not reset each
185
+ * other's failures. Every failed singleton when omitted.
186
+ */
187
+ function forgetFailures(nodes?: readonly ModuleNode[]): void;
126
188
  /** Clears every scope and the construction record. */
127
189
  function reset(): void;
128
190
  }
package/dist/errors.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { ServiceToken } from './token';
1
2
  /**
2
3
  * Base class of every error thrown by the Elysium framework.
3
4
  *
@@ -104,7 +105,13 @@ export declare class ServiceNotFoundError extends ElysiumError {
104
105
  export declare class ServiceNotReadyError extends ElysiumError {
105
106
  readonly service: string;
106
107
  readonly owner: string | null;
107
- constructor(service: string, owner?: string | null);
108
+ readonly token: ServiceToken;
109
+ /**
110
+ * @param service The service label, used in the message.
111
+ * @param owner The id of the module owning the service.
112
+ * @param token The token to pass to `Service.resolve()` to wait for the service.
113
+ */
114
+ constructor(service: string, owner?: string | null, token?: ServiceToken);
108
115
  }
109
116
  /**
110
117
  * Thrown when services depend on each other in a cycle.
@@ -151,9 +158,37 @@ export declare class InvalidControllerError extends ElysiumError {
151
158
  *
152
159
  * @author Axel Nana <axel.nana@workbud.com>
153
160
  */
154
- export declare class EventListenerNotSupportedError extends ElysiumError {
161
+ export declare class LifecycleListenerNotSupportedError extends ElysiumError {
155
162
  readonly className: string;
156
163
  readonly method: string;
157
164
  readonly moduleName: string;
158
165
  constructor(className: string, method: string, moduleName: string);
159
166
  }
167
+ /**
168
+ * Thrown when a request-scoped service is resolved outside a request.
169
+ *
170
+ * @author Axel Nana <axel.nana@workbud.com>
171
+ */
172
+ export declare class OutsideRequestScopeError extends ElysiumError {
173
+ readonly service: string;
174
+ constructor(service: string);
175
+ }
176
+ /**
177
+ * Thrown when a singleton service depends on a request-scoped one.
178
+ *
179
+ * @author Axel Nana <axel.nana@workbud.com>
180
+ */
181
+ export declare class ScopeMismatchError extends ElysiumError {
182
+ readonly service: string;
183
+ readonly dependency: string;
184
+ constructor(service: string, dependency: string);
185
+ }
186
+ /**
187
+ * Thrown when an interceptor calls `next()` more than once, which would run the handler twice.
188
+ *
189
+ * @author Axel Nana <axel.nana@workbud.com>
190
+ */
191
+ export declare class InterceptorNextCalledTwiceError extends ElysiumError {
192
+ readonly interceptor: string;
193
+ constructor(interceptor: string);
194
+ }
@@ -0,0 +1,78 @@
1
+ import type { Class } from 'type-fest';
2
+ import type { ModuleNode } from './module-graph';
3
+ /**
4
+ * The context an exception filter receives: the request, the response being built (`set`), the
5
+ * route and its parameters, as Elysia's error hook gives them.
6
+ * @author Axel Nana <axel.nana@workbud.com>
7
+ */
8
+ export type FilterContext = {
9
+ request: Request;
10
+ set: {
11
+ status?: number | string;
12
+ headers: Record<string, string | number>;
13
+ };
14
+ path: string;
15
+ route?: string;
16
+ params?: Record<string, string>;
17
+ query?: Record<string, unknown>;
18
+ [key: string]: unknown;
19
+ };
20
+ /**
21
+ * Turns some errors into responses. Return a value to send it (a body, a `Response`, or problem
22
+ * details, sent as `application/problem+json`), or `undefined` to pass the error to the next
23
+ * level. Filters are services: they can inject other services of their module.
24
+ * @author Axel Nana <axel.nana@workbud.com>
25
+ * @template TError The errors the filter handles.
26
+ */
27
+ export interface ExceptionFilter<TError = any> {
28
+ catch(error: TError, ctx: FilterContext): unknown | Promise<unknown>;
29
+ }
30
+ /**
31
+ * A class of exception filter.
32
+ * @author Axel Nana <axel.nana@workbud.com>
33
+ */
34
+ export type FilterClass = Class<ExceptionFilter>;
35
+ /**
36
+ * Declares which errors an exception filter handles, by class (`instanceof`). Without arguments,
37
+ * the filter handles every error. Only records metadata.
38
+ * @author Axel Nana <axel.nana@workbud.com>
39
+ * @param errors The error classes handled by the filter.
40
+ * @example
41
+ * ```ts
42
+ * @Catch(PaymentDeclinedError)
43
+ * class PaymentFilter implements ExceptionFilter<PaymentDeclinedError> {
44
+ * catch(error: PaymentDeclinedError) {
45
+ * return { type: 'payment-declined', title: 'Payment declined', status: 402, reason: error.reason };
46
+ * }
47
+ * }
48
+ * ```
49
+ */
50
+ export declare const Catch: (...errors: Class<unknown>[]) => ClassDecorator;
51
+ /**
52
+ * One level of filters (route, controller, module or app) and the module they run in.
53
+ * @author Axel Nana <axel.nana@workbud.com>
54
+ */
55
+ export type FilterLevel = {
56
+ filters: FilterClass[];
57
+ node: ModuleNode | null;
58
+ };
59
+ /** The filter levels below the app (route, controller, module) of each route handler. */
60
+ export declare const routeFilterLevels: WeakMap<Function, FilterLevel[]>;
61
+ /**
62
+ * The key under which a route stores its filter levels on the request context, once routed, so
63
+ * that the error hook finds them. Errors raised before routing (parsing) find them by route.
64
+ */
65
+ export declare const FILTER_LEVELS: unique symbol;
66
+ /**
67
+ * Runs the filters of each level in order, and returns the response of the first one that
68
+ * returns a value. Each filter runs in its module's context and in the request scope. A filter
69
+ * that throws is logged, and the original error goes on to the next filter.
70
+ * @author Axel Nana <axel.nana@workbud.com>
71
+ * @param levels The levels, most specific first.
72
+ * @param error The error to handle.
73
+ * @param ctx The error context of the request.
74
+ * @returns The response, or `undefined` when no filter handled the error.
75
+ */
76
+ export declare const runFilters: (levels: FilterLevel[], error: unknown, ctx: FilterContext) => Promise<{
77
+ response: unknown;
78
+ } | undefined>;
@@ -0,0 +1,80 @@
1
+ import { ElysiumError } from './errors';
2
+ /**
3
+ * Sends a request to an application and returns its response, e.g. `(r) => elysia.handle(r)` or a
4
+ * Worker's `fetch`.
5
+ * @author Axel Nana <axel.nana@workbud.com>
6
+ */
7
+ export type RequestHandler = (request: Request) => Promise<Response>;
8
+ /**
9
+ * Raised by `TestResponse.expectStatus()` when the response has another status. The message holds
10
+ * the response body, so a failing test shows why the request failed.
11
+ * @author Axel Nana <axel.nana@workbud.com>
12
+ */
13
+ export declare class UnexpectedStatusError extends ElysiumError {
14
+ readonly expected: number;
15
+ readonly actual: number;
16
+ readonly body: string;
17
+ constructor(expected: number, actual: number, body: string);
18
+ }
19
+ /**
20
+ * A response from the HTTP test client.
21
+ * @author Axel Nana <axel.nana@workbud.com>
22
+ */
23
+ export type TestResponse = Response & {
24
+ /** Parses the body as JSON. */
25
+ json<T = any>(): Promise<T>;
26
+ /**
27
+ * Checks the status, and throws an {@link UnexpectedStatusError} holding the body otherwise.
28
+ * @returns The response itself, with its body unread.
29
+ */
30
+ expectStatus(status: number): Promise<TestResponse>;
31
+ };
32
+ /**
33
+ * A request being built by the HTTP test client. Send it with `send()`, or await it directly.
34
+ * @author Axel Nana <axel.nana@workbud.com>
35
+ */
36
+ export declare class TestRequest implements PromiseLike<TestResponse> {
37
+ #private;
38
+ constructor(handle: RequestHandler, method: string, path: string, baseUrl: string);
39
+ /** Sets a request header. */
40
+ header(name: string, value: string): this;
41
+ /** Sets several request headers. */
42
+ headers(headers: Record<string, string>): this;
43
+ /** Adds query string parameters. Arrays add one parameter per value. */
44
+ query(params: Record<string, string | number | boolean | (string | number)[]>): this;
45
+ /** Sends a JSON body, with `content-type: application/json`. */
46
+ json(body: unknown): this;
47
+ /** Sends a URL-encoded form body. */
48
+ form(fields: Record<string, string>): this;
49
+ /** Sends a raw body: a string, bytes, a stream, `FormData` or `URLSearchParams`. */
50
+ body(body: BodyInit): this;
51
+ /** Builds the `Request` this builder describes, without sending it. */
52
+ toRequest(): Request;
53
+ /** Sends the request. */
54
+ send(): Promise<TestResponse>;
55
+ then<TResult1 = TestResponse, TResult2 = never>(onfulfilled?: ((value: TestResponse) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
56
+ }
57
+ /**
58
+ * An HTTP client that drives an application in memory, without a server.
59
+ * @author Axel Nana <axel.nana@workbud.com>
60
+ */
61
+ export type HttpTestClient = {
62
+ get(path: string): TestRequest;
63
+ post(path: string): TestRequest;
64
+ put(path: string): TestRequest;
65
+ patch(path: string): TestRequest;
66
+ delete(path: string): TestRequest;
67
+ head(path: string): TestRequest;
68
+ options(path: string): TestRequest;
69
+ /** Starts a request with any method. */
70
+ request(method: string, path: string): TestRequest;
71
+ /** Sends a prebuilt `Request` as is. */
72
+ send(request: Request): Promise<TestResponse>;
73
+ };
74
+ /**
75
+ * Creates an HTTP test client on top of a request handler.
76
+ * @author Axel Nana <axel.nana@workbud.com>
77
+ * @param handle Sends a request to the application, e.g. `(r) => elysia.handle(r)`.
78
+ * @param baseUrl The origin relative paths resolve against. Defaults to `http://localhost`.
79
+ */
80
+ export declare const createHttpClient: (handle: RequestHandler, baseUrl?: string) => HttpTestClient;