@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,815 @@
1
+ import { HTTPError } from 'elysia';
2
+ /**
3
+ * An error response in the RFC 9457 "problem details" format, sent as
4
+ * `application/problem+json`. Extension members sit at the top level, next to the standard ones.
5
+ * @author Axel Nana <axel.nana@workbud.com>
6
+ */
7
+ export type ProblemDetails = {
8
+ /** A URI reference that identifies the problem type: the slug of its code, under `typeBase`. */
9
+ type: string;
10
+ /** A short summary of the problem type. */
11
+ title: string;
12
+ /** The HTTP status code. */
13
+ status: number;
14
+ /** The stable machine-readable code of the problem, such as `NOT_FOUND`. */
15
+ code?: string;
16
+ /** An explanation specific to this occurrence. */
17
+ detail?: string;
18
+ /** A URI reference that identifies this occurrence: the request path. */
19
+ instance?: string;
20
+ /** The id of the request, to find it in the logs. */
21
+ requestId?: string;
22
+ [extension: string]: unknown;
23
+ };
24
+ /**
25
+ * One invalid value of a request, in the `errors` member of a validation problem.
26
+ * @author Axel Nana <axel.nana@workbud.com>
27
+ */
28
+ export type ProblemValidationError = {
29
+ /** A JSON Pointer (RFC 6901) to the value, rooted at the request part: `/body/email`. */
30
+ pointer: string;
31
+ /** What is wrong with it. */
32
+ message: string;
33
+ /** The rule that failed, such as `format` or `requiredProperties`. */
34
+ code?: string;
35
+ };
36
+ /**
37
+ * Options of the problem details rendering, set with `problems` on `@Application.register()`.
38
+ * @author Axel Nana <axel.nana@workbud.com>
39
+ */
40
+ export type ProblemOptions = {
41
+ /**
42
+ * The base of every problem `type`, such as `https://api.example.com/problems`: a problem of
43
+ * code `INSUFFICIENT_FUNDS` gets `https://api.example.com/problems/insufficient-funds`. Without
44
+ * it, `type` is the slug alone.
45
+ */
46
+ typeBase?: string;
47
+ /** The status of request validation errors. Defaults to 422. */
48
+ validationStatus?: 400 | 422;
49
+ /**
50
+ * The id of a request, served as `requestId` in its problems. Defaults to a valid
51
+ * `x-request-id` header, or a random UUID.
52
+ */
53
+ requestId?: (request: Request) => string;
54
+ };
55
+ /** The MIME type of problem details responses. */
56
+ export declare const PROBLEM_CONTENT_TYPE = "application/problem+json";
57
+ /**
58
+ * The standard reason phrase of an HTTP status, used as the default problem title.
59
+ * @author Axel Nana <axel.nana@workbud.com>
60
+ * @param status The HTTP status code.
61
+ */
62
+ export declare const statusTitle: (status: number) => string;
63
+ /**
64
+ * The problem codes of the application, declared by merging into this interface: one key per code.
65
+ * Once it has keys, `DomainError.for()` takes only those codes and the ones of the framework
66
+ * ({@link ElysiumProblemCode}); while it has none, any string is a code.
67
+ * @author Axel Nana <axel.nana@workbud.com>
68
+ * @example
69
+ * ```ts
70
+ * declare module '@elysiumjs/core' {
71
+ * interface ProblemCatalogue {
72
+ * INSUFFICIENT_FUNDS: true;
73
+ * ACCOUNT_CLOSED: true;
74
+ * }
75
+ * }
76
+ * ```
77
+ */
78
+ export interface ProblemCatalogue {
79
+ }
80
+ /**
81
+ * The problem codes the framework packages answer with. An application that keeps its own
82
+ * catalogue lists them too, so that its clients know every code.
83
+ * @author Axel Nana <axel.nana@workbud.com>
84
+ */
85
+ export declare const ELYSIUM_PROBLEM_CODES: {
86
+ /** 400: a request that cannot be read, such as a malformed body or invalid query. */
87
+ readonly BAD_REQUEST: 'BAD_REQUEST';
88
+ /** 400 or 422: request values that do not match their schema; carries `errors`. */
89
+ readonly VALIDATION_FAILED: 'VALIDATION_FAILED';
90
+ /** 401: the request has no valid credentials. */
91
+ readonly UNAUTHENTICATED: 'UNAUTHENTICATED';
92
+ /** 403: the caller may not do this. */
93
+ readonly FORBIDDEN: 'FORBIDDEN';
94
+ /** 404: no such resource or route. */
95
+ readonly NOT_FOUND: 'NOT_FOUND';
96
+ /** 409: the request conflicts with the current state. */
97
+ readonly CONFLICT: 'CONFLICT';
98
+ /** 413: the body is larger than the body limit. */
99
+ readonly PAYLOAD_TOO_LARGE: 'PAYLOAD_TOO_LARGE';
100
+ /** 429: carries `retryAfter` and the Retry-After header. */
101
+ readonly RATE_LIMITED: 'RATE_LIMITED';
102
+ /** 500: an unexpected failure. */
103
+ readonly INTERNAL_ERROR: 'INTERNAL_ERROR';
104
+ /** 403: the CSRF token of a cookie session is missing or does not match (themis). */
105
+ readonly CSRF_TOKEN_MISMATCH: 'CSRF_TOKEN_MISMATCH';
106
+ /** 403: a signed URL whose signature does not verify, or that expired (themis). */
107
+ readonly INVALID_SIGNATURE: 'INVALID_SIGNATURE';
108
+ /** 400: a passkey ceremony that did not verify (themis). */
109
+ readonly PASSKEY_VERIFICATION_FAILED: 'PASSKEY_VERIFICATION_FAILED';
110
+ /** 400: an OAuth sign-in that could not complete (themis). */
111
+ readonly OAUTH_CALLBACK_FAILED: 'OAUTH_CALLBACK_FAILED';
112
+ /** 428: an idempotent command called without an Idempotency-Key (moirai). */
113
+ readonly IDEMPOTENCY_KEY_REQUIRED: 'IDEMPOTENCY_KEY_REQUIRED';
114
+ /** 422: an Idempotency-Key used before for another request (moirai). */
115
+ readonly IDEMPOTENCY_KEY_REUSED: 'IDEMPOTENCY_KEY_REUSED';
116
+ /** 410: the flow does not exist or expired (ariadne). */
117
+ readonly FLOW_EXPIRED: 'FLOW_EXPIRED';
118
+ /** 410: the flow is finished (ariadne). */
119
+ readonly FLOW_COMPLETED: 'FLOW_COMPLETED';
120
+ /** 409: a step the flow does not accept now; carries `nextSteps`, `backSteps`, `answer` (ariadne). */
121
+ readonly FLOW_UNEXPECTED_STEP: 'FLOW_UNEXPECTED_STEP';
122
+ /** 409: another request is running the same step (ariadne). */
123
+ readonly FLOW_STEP_IN_PROGRESS: 'FLOW_STEP_IN_PROGRESS';
124
+ };
125
+ /**
126
+ * A problem code of the framework.
127
+ * @author Axel Nana <axel.nana@workbud.com>
128
+ */
129
+ export type ElysiumProblemCode = keyof typeof ELYSIUM_PROBLEM_CODES;
130
+ /**
131
+ * A problem code: one of {@link ProblemCatalogue} or of the framework, or any string while the
132
+ * catalogue is empty.
133
+ * @author Axel Nana <axel.nana@workbud.com>
134
+ */
135
+ export type ProblemCode = [keyof ProblemCatalogue] extends [never] ? string : (keyof ProblemCatalogue & string) | ElysiumProblemCode;
136
+ /**
137
+ * The `type` of a problem code: its kebab-case slug, under the base when one is set.
138
+ * `INSUFFICIENT_FUNDS` gives `insufficient-funds`, or `https://api.example.com/problems/insufficient-funds`.
139
+ * @author Axel Nana <axel.nana@workbud.com>
140
+ * @param code The problem code.
141
+ * @param base The `typeBase` of the application.
142
+ */
143
+ export declare const problemType: (code: string, base?: string) => string;
144
+ /**
145
+ * The options of a {@link DomainError}.
146
+ * @author Axel Nana <axel.nana@workbud.com>
147
+ */
148
+ export type DomainErrorOptions = {
149
+ /** The invalid values, served as `errors`. */
150
+ errors?: readonly ProblemValidationError[];
151
+ /** Seconds until the client may retry, served as `retryAfter` and the Retry-After header. */
152
+ retryAfterSeconds?: number;
153
+ /** Extra members of the problem; the standard members win over them. */
154
+ extensions?: Readonly<Record<string, unknown>>;
155
+ /** A title of its own. Defaults to the reason phrase of the status. */
156
+ title?: string;
157
+ /** The error that caused this one, kept for logs. */
158
+ cause?: unknown;
159
+ };
160
+ /**
161
+ * An error the application expects and reports to the client as problem details, and an Elysia
162
+ * `HTTPError`. Each error is a class of its own, made with `DomainError.for(code, status)`, so a
163
+ * signature names what can go wrong (`Promise<Account | AccountClosed>`).
164
+ *
165
+ * Handlers, commands, queries and flow steps return it (errors as values) or throw it; both send
166
+ * the same problem, roll back the unit of work, and commit nothing. A returned error joins the
167
+ * route's response type under its literal status, so typed clients see it. `message` is the
168
+ * detail shown to the client.
169
+ * @author Axel Nana <axel.nana@workbud.com>
170
+ * @template C The problem code.
171
+ * @template S The HTTP status.
172
+ * @example
173
+ * ```ts
174
+ * export class InsufficientFunds extends DomainError.for('INSUFFICIENT_FUNDS', 409) {
175
+ * constructor(missing: number) {
176
+ * super(`${missing} more are needed.`, { extensions: { missing } });
177
+ * }
178
+ * }
179
+ *
180
+ * public async pay(amount: number): Promise<Receipt | InsufficientFunds> {
181
+ * if (balance < amount) return new InsufficientFunds(amount - balance);
182
+ * ...
183
+ * }
184
+ * ```
185
+ */
186
+ export declare abstract class DomainError<C extends string = string, S extends number = number> extends HTTPError {
187
+ /** The base of `type`, set from `problems.typeBase` of the application. */
188
+ static typeBase?: string;
189
+ readonly code: C;
190
+ readonly status: S;
191
+ /** The detail shown to the client; undefined when none was given. */
192
+ readonly detail: string | undefined;
193
+ readonly title: string;
194
+ readonly errors: readonly ProblemValidationError[] | undefined;
195
+ readonly retryAfterSeconds: number | undefined;
196
+ readonly extensions: Readonly<Record<string, unknown>>;
197
+ readonly headers: Record<string, string> | undefined;
198
+ /** Protected: an error is created through its own class, made with `DomainError.for()`. */
199
+ protected constructor(code: C, status: S, detail?: string, options?: DomainErrorOptions);
200
+ /** The problem type: the slug of the code, under `typeBase`. */
201
+ get type(): string;
202
+ /**
203
+ * The base class of one error, with its code and status kept as literal types.
204
+ * @param code The problem code, from the {@link ProblemCatalogue} once it has keys.
205
+ * @param status The HTTP status.
206
+ */
207
+ static for<const C extends ProblemCode, const S extends number>(code: C, status: S): {
208
+ new (detail?: string, options?: DomainErrorOptions): {
209
+ name: string;
210
+ message: string;
211
+ stack?: string;
212
+ cause?: unknown;
213
+ value?(): import("elysia/types").MaybePromise<unknown>;
214
+ readonly code: C;
215
+ readonly status: S;
216
+ /** The detail shown to the client; undefined when none was given. */
217
+ readonly detail: string | undefined;
218
+ readonly title: string;
219
+ readonly errors: readonly ProblemValidationError[] | undefined;
220
+ readonly retryAfterSeconds: number | undefined;
221
+ readonly extensions: Readonly<Record<string, unknown>>;
222
+ readonly headers: Record<string, string> | undefined;
223
+ /** The problem type: the slug of the code, under `typeBase`. */
224
+ get type(): string;
225
+ /** The members besides type, code, status and detail: the extensions first, so that the standard ones win. */
226
+ extension(): Record<string, unknown>;
227
+ };
228
+ isError(error: unknown): error is Error;
229
+ isError(value: unknown): value is Error;
230
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
231
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
232
+ prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
233
+ stackTraceLimit: number;
234
+ id<const Type extends string>(code: Type): import("elysia").TaggedHTTPError<Type>;
235
+ id<const Type extends string, const S_1 extends number | keyof import("elysia").StatusMap>(code: Type, status: S_1): import("elysia").TaggedHTTPError<Type, {
236
+ readonly status: S_1 extends keyof import("elysia").StatusMap ? import("elysia").StatusMap[S_1] : S_1;
237
+ }>;
238
+ /** The base of `type`, set from `problems.typeBase` of the application. */
239
+ typeBase?: string;
240
+ for<const C extends ProblemCode, const S extends number>(code: C, status: S): /*elided*/ any;
241
+ };
242
+ /** The members besides type, code, status and detail: the extensions first, so that the standard ones win. */
243
+ extension(): Record<string, unknown>;
244
+ }
245
+ /**
246
+ * Checks whether a value is a {@link DomainError}, such as the result of a command.
247
+ * @author Axel Nana <axel.nana@workbud.com>
248
+ */
249
+ export declare const isDomainError: (value: unknown) => value is DomainError;
250
+ declare const BadRequestError_base: {
251
+ new (detail?: string, options?: DomainErrorOptions): {
252
+ name: string;
253
+ message: string;
254
+ stack?: string;
255
+ cause?: unknown;
256
+ value?(): import("elysia/types").MaybePromise<unknown>;
257
+ readonly code: "BAD_REQUEST";
258
+ readonly status: 400;
259
+ /** The detail shown to the client; undefined when none was given. */
260
+ readonly detail: string | undefined;
261
+ readonly title: string;
262
+ readonly errors: readonly ProblemValidationError[] | undefined;
263
+ readonly retryAfterSeconds: number | undefined;
264
+ readonly extensions: Readonly<Record<string, unknown>>;
265
+ readonly headers: Record<string, string> | undefined;
266
+ /** The problem type: the slug of the code, under `typeBase`. */
267
+ get type(): string;
268
+ /** The members besides type, code, status and detail: the extensions first, so that the standard ones win. */
269
+ extension(): Record<string, unknown>;
270
+ };
271
+ isError(error: unknown): error is Error;
272
+ isError(value: unknown): value is Error;
273
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
274
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
275
+ prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
276
+ stackTraceLimit: number;
277
+ id<const Type extends string>(code: Type): import("elysia").TaggedHTTPError<Type>;
278
+ id<const Type extends string, const S extends number | keyof import("elysia").StatusMap>(code: Type, status: S): import("elysia").TaggedHTTPError<Type, {
279
+ readonly status: S extends keyof import("elysia").StatusMap ? import("elysia").StatusMap[S] : S;
280
+ }>;
281
+ /** The base of `type`, set from `problems.typeBase` of the application. */
282
+ typeBase?: string;
283
+ /**
284
+ * The base class of one error, with its code and status kept as literal types.
285
+ * @param code The problem code, from the {@link ProblemCatalogue} once it has keys.
286
+ * @param status The HTTP status.
287
+ */
288
+ for<const C extends ProblemCode, const S extends number>(code: C, status: S): {
289
+ new (detail?: string, options?: DomainErrorOptions): {
290
+ name: string;
291
+ message: string;
292
+ stack?: string;
293
+ cause?: unknown;
294
+ value?(): import("elysia/types").MaybePromise<unknown>;
295
+ readonly code: C;
296
+ readonly status: S;
297
+ /** The detail shown to the client; undefined when none was given. */
298
+ readonly detail: string | undefined;
299
+ readonly title: string;
300
+ readonly errors: readonly ProblemValidationError[] | undefined;
301
+ readonly retryAfterSeconds: number | undefined;
302
+ readonly extensions: Readonly<Record<string, unknown>>;
303
+ readonly headers: Record<string, string> | undefined;
304
+ /** The problem type: the slug of the code, under `typeBase`. */
305
+ get type(): string;
306
+ /** The members besides type, code, status and detail: the extensions first, so that the standard ones win. */
307
+ extension(): Record<string, unknown>;
308
+ };
309
+ isError(error: unknown): error is Error;
310
+ isError(value: unknown): value is Error;
311
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
312
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
313
+ prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
314
+ stackTraceLimit: number;
315
+ id<const Type extends string>(code: Type): import("elysia").TaggedHTTPError<Type>;
316
+ id<const Type extends string, const S_1 extends number | keyof import("elysia").StatusMap>(code: Type, status: S_1): import("elysia").TaggedHTTPError<Type, {
317
+ readonly status: S_1 extends keyof import("elysia").StatusMap ? import("elysia").StatusMap[S_1] : S_1;
318
+ }>;
319
+ /** The base of `type`, set from `problems.typeBase` of the application. */
320
+ typeBase?: string;
321
+ for<const C extends ProblemCode, const S extends number>(code: C, status: S): /*elided*/ any;
322
+ };
323
+ };
324
+ /**
325
+ * A request that cannot be read: a 400 problem.
326
+ * @author Axel Nana <axel.nana@workbud.com>
327
+ */
328
+ export declare class BadRequestError extends BadRequestError_base {
329
+ }
330
+ declare const ValidationFailedError_base: {
331
+ new (detail?: string, options?: DomainErrorOptions): {
332
+ name: string;
333
+ message: string;
334
+ stack?: string;
335
+ cause?: unknown;
336
+ value?(): import("elysia/types").MaybePromise<unknown>;
337
+ readonly code: "VALIDATION_FAILED";
338
+ readonly status: 422;
339
+ /** The detail shown to the client; undefined when none was given. */
340
+ readonly detail: string | undefined;
341
+ readonly title: string;
342
+ readonly errors: readonly ProblemValidationError[] | undefined;
343
+ readonly retryAfterSeconds: number | undefined;
344
+ readonly extensions: Readonly<Record<string, unknown>>;
345
+ readonly headers: Record<string, string> | undefined;
346
+ /** The problem type: the slug of the code, under `typeBase`. */
347
+ get type(): string;
348
+ /** The members besides type, code, status and detail: the extensions first, so that the standard ones win. */
349
+ extension(): Record<string, unknown>;
350
+ };
351
+ isError(error: unknown): error is Error;
352
+ isError(value: unknown): value is Error;
353
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
354
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
355
+ prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
356
+ stackTraceLimit: number;
357
+ id<const Type extends string>(code: Type): import("elysia").TaggedHTTPError<Type>;
358
+ id<const Type extends string, const S extends number | keyof import("elysia").StatusMap>(code: Type, status: S): import("elysia").TaggedHTTPError<Type, {
359
+ readonly status: S extends keyof import("elysia").StatusMap ? import("elysia").StatusMap[S] : S;
360
+ }>;
361
+ /** The base of `type`, set from `problems.typeBase` of the application. */
362
+ typeBase?: string;
363
+ /**
364
+ * The base class of one error, with its code and status kept as literal types.
365
+ * @param code The problem code, from the {@link ProblemCatalogue} once it has keys.
366
+ * @param status The HTTP status.
367
+ */
368
+ for<const C extends ProblemCode, const S extends number>(code: C, status: S): {
369
+ new (detail?: string, options?: DomainErrorOptions): {
370
+ name: string;
371
+ message: string;
372
+ stack?: string;
373
+ cause?: unknown;
374
+ value?(): import("elysia/types").MaybePromise<unknown>;
375
+ readonly code: C;
376
+ readonly status: S;
377
+ /** The detail shown to the client; undefined when none was given. */
378
+ readonly detail: string | undefined;
379
+ readonly title: string;
380
+ readonly errors: readonly ProblemValidationError[] | undefined;
381
+ readonly retryAfterSeconds: number | undefined;
382
+ readonly extensions: Readonly<Record<string, unknown>>;
383
+ readonly headers: Record<string, string> | undefined;
384
+ /** The problem type: the slug of the code, under `typeBase`. */
385
+ get type(): string;
386
+ /** The members besides type, code, status and detail: the extensions first, so that the standard ones win. */
387
+ extension(): Record<string, unknown>;
388
+ };
389
+ isError(error: unknown): error is Error;
390
+ isError(value: unknown): value is Error;
391
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
392
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
393
+ prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
394
+ stackTraceLimit: number;
395
+ id<const Type extends string>(code: Type): import("elysia").TaggedHTTPError<Type>;
396
+ id<const Type extends string, const S_1 extends number | keyof import("elysia").StatusMap>(code: Type, status: S_1): import("elysia").TaggedHTTPError<Type, {
397
+ readonly status: S_1 extends keyof import("elysia").StatusMap ? import("elysia").StatusMap[S_1] : S_1;
398
+ }>;
399
+ /** The base of `type`, set from `problems.typeBase` of the application. */
400
+ typeBase?: string;
401
+ for<const C extends ProblemCode, const S extends number>(code: C, status: S): /*elided*/ any;
402
+ };
403
+ };
404
+ /**
405
+ * Request values that do not match their schema: a 422 problem with one entry per mistake.
406
+ * @author Axel Nana <axel.nana@workbud.com>
407
+ */
408
+ export declare class ValidationFailedError extends ValidationFailedError_base {
409
+ constructor(errors: readonly ProblemValidationError[], detail?: string);
410
+ }
411
+ declare const UnauthenticatedError_base: {
412
+ new (detail?: string, options?: DomainErrorOptions): {
413
+ name: string;
414
+ message: string;
415
+ stack?: string;
416
+ cause?: unknown;
417
+ value?(): import("elysia/types").MaybePromise<unknown>;
418
+ readonly code: "UNAUTHENTICATED";
419
+ readonly status: 401;
420
+ /** The detail shown to the client; undefined when none was given. */
421
+ readonly detail: string | undefined;
422
+ readonly title: string;
423
+ readonly errors: readonly ProblemValidationError[] | undefined;
424
+ readonly retryAfterSeconds: number | undefined;
425
+ readonly extensions: Readonly<Record<string, unknown>>;
426
+ readonly headers: Record<string, string> | undefined;
427
+ /** The problem type: the slug of the code, under `typeBase`. */
428
+ get type(): string;
429
+ /** The members besides type, code, status and detail: the extensions first, so that the standard ones win. */
430
+ extension(): Record<string, unknown>;
431
+ };
432
+ isError(error: unknown): error is Error;
433
+ isError(value: unknown): value is Error;
434
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
435
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
436
+ prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
437
+ stackTraceLimit: number;
438
+ id<const Type extends string>(code: Type): import("elysia").TaggedHTTPError<Type>;
439
+ id<const Type extends string, const S extends number | keyof import("elysia").StatusMap>(code: Type, status: S): import("elysia").TaggedHTTPError<Type, {
440
+ readonly status: S extends keyof import("elysia").StatusMap ? import("elysia").StatusMap[S] : S;
441
+ }>;
442
+ /** The base of `type`, set from `problems.typeBase` of the application. */
443
+ typeBase?: string;
444
+ /**
445
+ * The base class of one error, with its code and status kept as literal types.
446
+ * @param code The problem code, from the {@link ProblemCatalogue} once it has keys.
447
+ * @param status The HTTP status.
448
+ */
449
+ for<const C extends ProblemCode, const S extends number>(code: C, status: S): {
450
+ new (detail?: string, options?: DomainErrorOptions): {
451
+ name: string;
452
+ message: string;
453
+ stack?: string;
454
+ cause?: unknown;
455
+ value?(): import("elysia/types").MaybePromise<unknown>;
456
+ readonly code: C;
457
+ readonly status: S;
458
+ /** The detail shown to the client; undefined when none was given. */
459
+ readonly detail: string | undefined;
460
+ readonly title: string;
461
+ readonly errors: readonly ProblemValidationError[] | undefined;
462
+ readonly retryAfterSeconds: number | undefined;
463
+ readonly extensions: Readonly<Record<string, unknown>>;
464
+ readonly headers: Record<string, string> | undefined;
465
+ /** The problem type: the slug of the code, under `typeBase`. */
466
+ get type(): string;
467
+ /** The members besides type, code, status and detail: the extensions first, so that the standard ones win. */
468
+ extension(): Record<string, unknown>;
469
+ };
470
+ isError(error: unknown): error is Error;
471
+ isError(value: unknown): value is Error;
472
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
473
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
474
+ prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
475
+ stackTraceLimit: number;
476
+ id<const Type extends string>(code: Type): import("elysia").TaggedHTTPError<Type>;
477
+ id<const Type extends string, const S_1 extends number | keyof import("elysia").StatusMap>(code: Type, status: S_1): import("elysia").TaggedHTTPError<Type, {
478
+ readonly status: S_1 extends keyof import("elysia").StatusMap ? import("elysia").StatusMap[S_1] : S_1;
479
+ }>;
480
+ /** The base of `type`, set from `problems.typeBase` of the application. */
481
+ typeBase?: string;
482
+ for<const C extends ProblemCode, const S extends number>(code: C, status: S): /*elided*/ any;
483
+ };
484
+ };
485
+ /**
486
+ * A request without valid credentials: a 401 problem.
487
+ * @author Axel Nana <axel.nana@workbud.com>
488
+ */
489
+ export declare class UnauthenticatedError extends UnauthenticatedError_base {
490
+ constructor(detail?: string);
491
+ }
492
+ declare const ForbiddenError_base: {
493
+ new (detail?: string, options?: DomainErrorOptions): {
494
+ name: string;
495
+ message: string;
496
+ stack?: string;
497
+ cause?: unknown;
498
+ value?(): import("elysia/types").MaybePromise<unknown>;
499
+ readonly code: "FORBIDDEN";
500
+ readonly status: 403;
501
+ /** The detail shown to the client; undefined when none was given. */
502
+ readonly detail: string | undefined;
503
+ readonly title: string;
504
+ readonly errors: readonly ProblemValidationError[] | undefined;
505
+ readonly retryAfterSeconds: number | undefined;
506
+ readonly extensions: Readonly<Record<string, unknown>>;
507
+ readonly headers: Record<string, string> | undefined;
508
+ /** The problem type: the slug of the code, under `typeBase`. */
509
+ get type(): string;
510
+ /** The members besides type, code, status and detail: the extensions first, so that the standard ones win. */
511
+ extension(): Record<string, unknown>;
512
+ };
513
+ isError(error: unknown): error is Error;
514
+ isError(value: unknown): value is Error;
515
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
516
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
517
+ prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
518
+ stackTraceLimit: number;
519
+ id<const Type extends string>(code: Type): import("elysia").TaggedHTTPError<Type>;
520
+ id<const Type extends string, const S extends number | keyof import("elysia").StatusMap>(code: Type, status: S): import("elysia").TaggedHTTPError<Type, {
521
+ readonly status: S extends keyof import("elysia").StatusMap ? import("elysia").StatusMap[S] : S;
522
+ }>;
523
+ /** The base of `type`, set from `problems.typeBase` of the application. */
524
+ typeBase?: string;
525
+ /**
526
+ * The base class of one error, with its code and status kept as literal types.
527
+ * @param code The problem code, from the {@link ProblemCatalogue} once it has keys.
528
+ * @param status The HTTP status.
529
+ */
530
+ for<const C extends ProblemCode, const S extends number>(code: C, status: S): {
531
+ new (detail?: string, options?: DomainErrorOptions): {
532
+ name: string;
533
+ message: string;
534
+ stack?: string;
535
+ cause?: unknown;
536
+ value?(): import("elysia/types").MaybePromise<unknown>;
537
+ readonly code: C;
538
+ readonly status: S;
539
+ /** The detail shown to the client; undefined when none was given. */
540
+ readonly detail: string | undefined;
541
+ readonly title: string;
542
+ readonly errors: readonly ProblemValidationError[] | undefined;
543
+ readonly retryAfterSeconds: number | undefined;
544
+ readonly extensions: Readonly<Record<string, unknown>>;
545
+ readonly headers: Record<string, string> | undefined;
546
+ /** The problem type: the slug of the code, under `typeBase`. */
547
+ get type(): string;
548
+ /** The members besides type, code, status and detail: the extensions first, so that the standard ones win. */
549
+ extension(): Record<string, unknown>;
550
+ };
551
+ isError(error: unknown): error is Error;
552
+ isError(value: unknown): value is Error;
553
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
554
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
555
+ prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
556
+ stackTraceLimit: number;
557
+ id<const Type extends string>(code: Type): import("elysia").TaggedHTTPError<Type>;
558
+ id<const Type extends string, const S_1 extends number | keyof import("elysia").StatusMap>(code: Type, status: S_1): import("elysia").TaggedHTTPError<Type, {
559
+ readonly status: S_1 extends keyof import("elysia").StatusMap ? import("elysia").StatusMap[S_1] : S_1;
560
+ }>;
561
+ /** The base of `type`, set from `problems.typeBase` of the application. */
562
+ typeBase?: string;
563
+ for<const C extends ProblemCode, const S extends number>(code: C, status: S): /*elided*/ any;
564
+ };
565
+ };
566
+ /**
567
+ * A caller that may not do this: a 403 problem.
568
+ * @author Axel Nana <axel.nana@workbud.com>
569
+ */
570
+ export declare class ForbiddenError extends ForbiddenError_base {
571
+ }
572
+ declare const NotFoundError_base: {
573
+ new (detail?: string, options?: DomainErrorOptions): {
574
+ name: string;
575
+ message: string;
576
+ stack?: string;
577
+ cause?: unknown;
578
+ value?(): import("elysia/types").MaybePromise<unknown>;
579
+ readonly code: "NOT_FOUND";
580
+ readonly status: 404;
581
+ /** The detail shown to the client; undefined when none was given. */
582
+ readonly detail: string | undefined;
583
+ readonly title: string;
584
+ readonly errors: readonly ProblemValidationError[] | undefined;
585
+ readonly retryAfterSeconds: number | undefined;
586
+ readonly extensions: Readonly<Record<string, unknown>>;
587
+ readonly headers: Record<string, string> | undefined;
588
+ /** The problem type: the slug of the code, under `typeBase`. */
589
+ get type(): string;
590
+ /** The members besides type, code, status and detail: the extensions first, so that the standard ones win. */
591
+ extension(): Record<string, unknown>;
592
+ };
593
+ isError(error: unknown): error is Error;
594
+ isError(value: unknown): value is Error;
595
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
596
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
597
+ prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
598
+ stackTraceLimit: number;
599
+ id<const Type extends string>(code: Type): import("elysia").TaggedHTTPError<Type>;
600
+ id<const Type extends string, const S extends number | keyof import("elysia").StatusMap>(code: Type, status: S): import("elysia").TaggedHTTPError<Type, {
601
+ readonly status: S extends keyof import("elysia").StatusMap ? import("elysia").StatusMap[S] : S;
602
+ }>;
603
+ /** The base of `type`, set from `problems.typeBase` of the application. */
604
+ typeBase?: string;
605
+ /**
606
+ * The base class of one error, with its code and status kept as literal types.
607
+ * @param code The problem code, from the {@link ProblemCatalogue} once it has keys.
608
+ * @param status The HTTP status.
609
+ */
610
+ for<const C extends ProblemCode, const S extends number>(code: C, status: S): {
611
+ new (detail?: string, options?: DomainErrorOptions): {
612
+ name: string;
613
+ message: string;
614
+ stack?: string;
615
+ cause?: unknown;
616
+ value?(): import("elysia/types").MaybePromise<unknown>;
617
+ readonly code: C;
618
+ readonly status: S;
619
+ /** The detail shown to the client; undefined when none was given. */
620
+ readonly detail: string | undefined;
621
+ readonly title: string;
622
+ readonly errors: readonly ProblemValidationError[] | undefined;
623
+ readonly retryAfterSeconds: number | undefined;
624
+ readonly extensions: Readonly<Record<string, unknown>>;
625
+ readonly headers: Record<string, string> | undefined;
626
+ /** The problem type: the slug of the code, under `typeBase`. */
627
+ get type(): string;
628
+ /** The members besides type, code, status and detail: the extensions first, so that the standard ones win. */
629
+ extension(): Record<string, unknown>;
630
+ };
631
+ isError(error: unknown): error is Error;
632
+ isError(value: unknown): value is Error;
633
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
634
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
635
+ prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
636
+ stackTraceLimit: number;
637
+ id<const Type extends string>(code: Type): import("elysia").TaggedHTTPError<Type>;
638
+ id<const Type extends string, const S_1 extends number | keyof import("elysia").StatusMap>(code: Type, status: S_1): import("elysia").TaggedHTTPError<Type, {
639
+ readonly status: S_1 extends keyof import("elysia").StatusMap ? import("elysia").StatusMap[S_1] : S_1;
640
+ }>;
641
+ /** The base of `type`, set from `problems.typeBase` of the application. */
642
+ typeBase?: string;
643
+ for<const C extends ProblemCode, const S extends number>(code: C, status: S): /*elided*/ any;
644
+ };
645
+ };
646
+ /**
647
+ * A resource that does not exist: a 404 problem.
648
+ * @author Axel Nana <axel.nana@workbud.com>
649
+ */
650
+ export declare class NotFoundError extends NotFoundError_base {
651
+ }
652
+ declare const ConflictError_base: {
653
+ new (detail?: string, options?: DomainErrorOptions): {
654
+ name: string;
655
+ message: string;
656
+ stack?: string;
657
+ cause?: unknown;
658
+ value?(): import("elysia/types").MaybePromise<unknown>;
659
+ readonly code: "CONFLICT";
660
+ readonly status: 409;
661
+ /** The detail shown to the client; undefined when none was given. */
662
+ readonly detail: string | undefined;
663
+ readonly title: string;
664
+ readonly errors: readonly ProblemValidationError[] | undefined;
665
+ readonly retryAfterSeconds: number | undefined;
666
+ readonly extensions: Readonly<Record<string, unknown>>;
667
+ readonly headers: Record<string, string> | undefined;
668
+ /** The problem type: the slug of the code, under `typeBase`. */
669
+ get type(): string;
670
+ /** The members besides type, code, status and detail: the extensions first, so that the standard ones win. */
671
+ extension(): Record<string, unknown>;
672
+ };
673
+ isError(error: unknown): error is Error;
674
+ isError(value: unknown): value is Error;
675
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
676
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
677
+ prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
678
+ stackTraceLimit: number;
679
+ id<const Type extends string>(code: Type): import("elysia").TaggedHTTPError<Type>;
680
+ id<const Type extends string, const S extends number | keyof import("elysia").StatusMap>(code: Type, status: S): import("elysia").TaggedHTTPError<Type, {
681
+ readonly status: S extends keyof import("elysia").StatusMap ? import("elysia").StatusMap[S] : S;
682
+ }>;
683
+ /** The base of `type`, set from `problems.typeBase` of the application. */
684
+ typeBase?: string;
685
+ /**
686
+ * The base class of one error, with its code and status kept as literal types.
687
+ * @param code The problem code, from the {@link ProblemCatalogue} once it has keys.
688
+ * @param status The HTTP status.
689
+ */
690
+ for<const C extends ProblemCode, const S extends number>(code: C, status: S): {
691
+ new (detail?: string, options?: DomainErrorOptions): {
692
+ name: string;
693
+ message: string;
694
+ stack?: string;
695
+ cause?: unknown;
696
+ value?(): import("elysia/types").MaybePromise<unknown>;
697
+ readonly code: C;
698
+ readonly status: S;
699
+ /** The detail shown to the client; undefined when none was given. */
700
+ readonly detail: string | undefined;
701
+ readonly title: string;
702
+ readonly errors: readonly ProblemValidationError[] | undefined;
703
+ readonly retryAfterSeconds: number | undefined;
704
+ readonly extensions: Readonly<Record<string, unknown>>;
705
+ readonly headers: Record<string, string> | undefined;
706
+ /** The problem type: the slug of the code, under `typeBase`. */
707
+ get type(): string;
708
+ /** The members besides type, code, status and detail: the extensions first, so that the standard ones win. */
709
+ extension(): Record<string, unknown>;
710
+ };
711
+ isError(error: unknown): error is Error;
712
+ isError(value: unknown): value is Error;
713
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
714
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
715
+ prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
716
+ stackTraceLimit: number;
717
+ id<const Type extends string>(code: Type): import("elysia").TaggedHTTPError<Type>;
718
+ id<const Type extends string, const S_1 extends number | keyof import("elysia").StatusMap>(code: Type, status: S_1): import("elysia").TaggedHTTPError<Type, {
719
+ readonly status: S_1 extends keyof import("elysia").StatusMap ? import("elysia").StatusMap[S_1] : S_1;
720
+ }>;
721
+ /** The base of `type`, set from `problems.typeBase` of the application. */
722
+ typeBase?: string;
723
+ for<const C extends ProblemCode, const S extends number>(code: C, status: S): /*elided*/ any;
724
+ };
725
+ };
726
+ /**
727
+ * A request that conflicts with the current state: a 409 problem.
728
+ * @author Axel Nana <axel.nana@workbud.com>
729
+ */
730
+ export declare class ConflictError extends ConflictError_base {
731
+ }
732
+ /**
733
+ * Checks whether a value is a problem details object: a `status` number and a `title` string.
734
+ * @author Axel Nana <axel.nana@workbud.com>
735
+ */
736
+ export declare const isProblemDetails: (value: unknown) => value is ProblemDetails;
737
+ /**
738
+ * Elysia wraps anything thrown while parsing the body (such as a body over the size limit) in a
739
+ * parse error. Returns the wrapped error when it is a {@link DomainError}, so that it keeps its
740
+ * own status, and the error itself otherwise.
741
+ * @author Axel Nana <axel.nana@workbud.com>
742
+ * @param error The thrown value.
743
+ */
744
+ export declare const unwrapParseError: (error: unknown) => unknown;
745
+ /**
746
+ * Turns the errors of an Elysia validation error into problem `errors`, one per invalid value.
747
+ * A missing required property is reported at the property's own pointer.
748
+ * @author Axel Nana <axel.nana@workbud.com>
749
+ * @param part The request part: `body`, `query`, `params`, `headers` or `cookie`.
750
+ * @param errors The validator errors (`ValidationError.all`).
751
+ */
752
+ export declare const validationErrors: (part: string, errors: unknown[]) => ProblemValidationError[];
753
+ /**
754
+ * The context {@link toProblem} needs about the request and the application.
755
+ * @author Axel Nana <axel.nana@workbud.com>
756
+ */
757
+ export type ProblemContext = Omit<ProblemOptions, 'requestId'> & {
758
+ /** The request path, used as `instance`. */
759
+ path?: string;
760
+ /** The id of the request, served as `requestId`. */
761
+ requestId?: string;
762
+ /** Whether to include the detail and the stack of server errors. */
763
+ development: boolean;
764
+ };
765
+ /**
766
+ * The id of a request: the one `make` gives, a valid `x-request-id` header, or a random UUID.
767
+ * The same request always gets the same id.
768
+ * @author Axel Nana <axel.nana@workbud.com>
769
+ * @param request The request.
770
+ * @param make The `requestId` option of the application.
771
+ */
772
+ export declare const requestIdOf: (request: Request, make?: (request: Request) => string) => string;
773
+ /**
774
+ * Turns any error thrown or returned while handling a request into problem details. Every problem
775
+ * carries `code`, and the `requestId` and `instance` of the request when they are known.
776
+ *
777
+ * - A {@link DomainError} keeps its code, status, title, detail and members.
778
+ * - Elysia's request validation errors give `VALIDATION_FAILED` at `validationStatus` (422), with
779
+ * an `errors` list.
780
+ * - Elysia's not found, parse and invalid cookie signature errors give `NOT_FOUND` (404) and
781
+ * `BAD_REQUEST` (400).
782
+ * - Anything else is an `INTERNAL_ERROR` (500) whose detail and stack are only shown in
783
+ * development.
784
+ * @author Axel Nana <axel.nana@workbud.com>
785
+ * @param error The thrown or returned value.
786
+ * @param context The request and the application's problem options.
787
+ */
788
+ export declare const toProblem: (error: unknown, context: ProblemContext) => ProblemDetails;
789
+ /**
790
+ * Builds the `application/problem+json` response of a problem.
791
+ * @author Axel Nana <axel.nana@workbud.com>
792
+ * @param problem The problem details.
793
+ * @param headers Extra headers, such as `Retry-After`.
794
+ */
795
+ export declare const problemResponse: (problem: ProblemDetails, headers?: Record<string, string>) => Response;
796
+ /**
797
+ * The OpenAPI schema of problem details, referenced by the default error responses of each route.
798
+ * @author Axel Nana <axel.nana@workbud.com>
799
+ */
800
+ export declare const ProblemDetailsSchema: import("typebox").TObject<{
801
+ type: import("typebox").TString;
802
+ title: import("typebox").TString;
803
+ status: import("typebox").TInteger;
804
+ code: import("typebox").TString;
805
+ detail: import("typebox").TOptional<import("typebox").TString>;
806
+ instance: import("typebox").TOptional<import("typebox").TString>;
807
+ requestId: import("typebox").TOptional<import("typebox").TString>;
808
+ retryAfter: import("typebox").TOptional<import("typebox").TInteger>;
809
+ errors: import("typebox").TOptional<import("typebox").TArray<import("typebox").TObject<{
810
+ pointer: import("typebox").TString;
811
+ message: import("typebox").TString;
812
+ code: import("typebox").TOptional<import("typebox").TString>;
813
+ }>>>;
814
+ }>;
815
+ export {};