@zajno/common 2.5.0 → 2.6.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.
- package/cjs/api/builder.js +30 -0
- package/cjs/api/builder.js.map +1 -0
- package/cjs/api/call.js +14 -7
- package/cjs/api/call.js.map +1 -1
- package/cjs/api/endpoint.js +65 -58
- package/cjs/api/endpoint.js.map +1 -1
- package/cjs/api/endpoint.types.js +0 -8
- package/cjs/api/endpoint.types.js.map +1 -1
- package/cjs/api/error.js +12 -0
- package/cjs/api/error.js.map +1 -1
- package/cjs/api/extensions/contentType.js +39 -0
- package/cjs/api/extensions/contentType.js.map +1 -0
- package/cjs/api/extensions/index.js +6 -0
- package/cjs/api/extensions/index.js.map +1 -0
- package/cjs/api/extensions/validation.js +27 -0
- package/cjs/api/extensions/validation.js.map +1 -0
- package/cjs/api/helpers.js +3 -2
- package/cjs/api/helpers.js.map +1 -1
- package/cjs/api/logging.js +23 -0
- package/cjs/api/logging.js.map +1 -1
- package/esm/api/builder.js +26 -0
- package/esm/api/builder.js.map +1 -0
- package/esm/api/call.js +14 -7
- package/esm/api/call.js.map +1 -1
- package/esm/api/endpoint.js +65 -57
- package/esm/api/endpoint.js.map +1 -1
- package/esm/api/endpoint.types.js +1 -7
- package/esm/api/endpoint.types.js.map +1 -1
- package/esm/api/error.js +12 -0
- package/esm/api/error.js.map +1 -1
- package/esm/api/extensions/contentType.js +36 -0
- package/esm/api/extensions/contentType.js.map +1 -0
- package/esm/api/extensions/index.js +3 -0
- package/esm/api/extensions/index.js.map +1 -0
- package/esm/api/extensions/validation.js +24 -0
- package/esm/api/extensions/validation.js.map +1 -0
- package/esm/api/helpers.js +3 -2
- package/esm/api/helpers.js.map +1 -1
- package/esm/api/logging.js +23 -0
- package/esm/api/logging.js.map +1 -1
- package/package.json +9 -16
- package/tsconfig.cjs.tsbuildinfo +1 -1
- package/tsconfig.esm.tsbuildinfo +1 -1
- package/tsconfig.types.tsbuildinfo +1 -1
- package/types/api/builder.d.ts +16 -0
- package/types/api/call.d.ts +17 -4
- package/types/api/endpoint.d.ts +39 -30
- package/types/api/endpoint.types.d.ts +23 -12
- package/types/api/error.d.ts +2 -0
- package/types/api/extensions/contentType.d.ts +22 -0
- package/types/api/extensions/index.d.ts +2 -0
- package/types/api/extensions/validation.d.ts +12 -0
- package/types/api/helpers.d.ts +1 -1
- package/types/api/logging.d.ts +27 -1
package/types/api/endpoint.d.ts
CHANGED
|
@@ -1,38 +1,47 @@
|
|
|
1
|
-
import { AnyObject } from '../types/index.js';
|
|
2
1
|
import { Path } from '../structures/path/index.js';
|
|
3
2
|
import { EndpointMethods } from './methods.js';
|
|
4
|
-
import { IEndpointInfo } from './endpoint.types.js';
|
|
3
|
+
import type { IEndpointInfo } from './endpoint.types.js';
|
|
4
|
+
/** @import { buildApiCaller } from "./call" */
|
|
5
|
+
/** Not an ideal way to merge type because overlapping fields will be incorrectly overridden.
|
|
6
|
+
*
|
|
7
|
+
* Extractor types works okay unless there're no overlapping fields when extending endpoint type.
|
|
8
|
+
*
|
|
9
|
+
* That's a trade-off, because in case of `Omit` usage extractor type are still okay but chaining like with `asForm()` is broken. */
|
|
10
|
+
type Merge<T, K> = K & T;
|
|
5
11
|
export type { IEndpointInfo };
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
get
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
static delete<TOut>(displayName?: string): ApiEndpoint<null, TOut, readonly [], object, any, AnyObject>;
|
|
12
|
+
/**
|
|
13
|
+
* Defines REST API endpoint.
|
|
14
|
+
*
|
|
15
|
+
* Endpoint object can be used in a caller method (see {@link buildApiCaller}) to make a request.
|
|
16
|
+
*
|
|
17
|
+
* Basic definition {@link IEndpointInfo} which can be extended with additional properties and chaining mutation methods, see {@link IEndpointInfo.IForm} for example.
|
|
18
|
+
*/
|
|
19
|
+
export interface ApiEndpoint extends IEndpointInfo {
|
|
20
|
+
/** Applies specified HTTP method */
|
|
21
|
+
withMethod(method: EndpointMethods): this;
|
|
22
|
+
/** Applies GET HTTP method and specifies output type */
|
|
23
|
+
get<TOut>(): Merge<this, IEndpointInfo.IOut<TOut>>;
|
|
24
|
+
/** Applies PUT HTTP method and specifies input and output types */
|
|
25
|
+
put<TIn extends object | null, TOut>(): Merge<this, IEndpointInfo.IIn<TIn> & IEndpointInfo.IOut<TOut>>;
|
|
26
|
+
/** Applies POST HTTP method and specifies input and output types */
|
|
27
|
+
post<TIn extends object | null, TOut>(): Merge<this, IEndpointInfo.IIn<TIn> & IEndpointInfo.IOut<TOut>>;
|
|
28
|
+
/** Applies DELETE HTTP method and specifies output type */
|
|
29
|
+
delete<TOut>(): Merge<this, IEndpointInfo.IOut<TOut>>;
|
|
25
30
|
/** Applies a type based on {@link Path.IBuilder} type by accepting arguments for {@link Path.construct} function */
|
|
26
|
-
withPath<P extends Path.BaseInput[]>(...path: P):
|
|
31
|
+
withPath<P extends Path.BaseInput[]>(...path: P): Merge<this, IEndpointInfo.IPath<Path.ExtractArgs<Path.CombineBuilders<P>>>>;
|
|
27
32
|
/** Applies query type and also store query keys to make api caller be able to distinguish which keys should be ejected from request body. */
|
|
28
|
-
withQuery<TQ extends object>(...queryKeys: (string & keyof TQ)[]):
|
|
33
|
+
withQuery<TQ extends object>(...queryKeys: (string & keyof TQ)[]): Merge<this, IEndpointInfo.IQuery<TQ>>;
|
|
29
34
|
/** Applies error type, optionally stores error processor. */
|
|
30
|
-
withErrors<TErr>(errorProcessor?: (err: TErr) => void):
|
|
35
|
+
withErrors<TErr>(errorProcessor?: (err: TErr) => void): Merge<this, IEndpointInfo.IErrors<TErr>>;
|
|
31
36
|
/** Applies headers type. */
|
|
32
|
-
withHeaders<THeads>(_headersMarker?: THeads):
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
withHeaders<THeads>(_headersMarker?: THeads): Merge<this, IEndpointInfo.IHeaders<THeads>>;
|
|
38
|
+
}
|
|
39
|
+
export declare namespace ApiEndpoint {
|
|
40
|
+
function isEndpoint(obj: any): obj is ApiEndpoint;
|
|
41
|
+
interface IBuilder<T extends ApiEndpoint = ApiEndpoint> {
|
|
42
|
+
(displayName?: string): T;
|
|
43
|
+
extend<TExt>(extender: (base: T) => T & TExt): IBuilder<T & TExt>;
|
|
44
|
+
}
|
|
45
|
+
type IBuilderExtender<T> = <TBase extends ApiEndpoint>(base: TBase) => TBase & T;
|
|
46
|
+
const create: IBuilder<ApiEndpoint>;
|
|
38
47
|
}
|
|
@@ -1,22 +1,32 @@
|
|
|
1
|
-
import { Path } from '../structures/path/index.js';
|
|
2
|
-
import { AnyObject, Coalesce, EmptyObjectNullable } from '../types/misc.js';
|
|
3
|
-
import { EndpointMethods } from './methods.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
readonly displayName?: string;
|
|
9
|
-
readonly errorProcessor?: (err?: any) => void;
|
|
10
|
-
readonly queryKeys?: string[];
|
|
1
|
+
import type { Path } from '../structures/path/index.js';
|
|
2
|
+
import type { AnyObject, Coalesce, EmptyObjectNullable } from '../types/misc.js';
|
|
3
|
+
import type { EndpointMethods } from './methods.js';
|
|
4
|
+
/**
|
|
5
|
+
* Definition of an abstract REST API endpoint.
|
|
6
|
+
*/
|
|
7
|
+
export interface IEndpointInfo extends IEndpointInfo.Base, IEndpointInfo.IPath<readonly string[]>, IEndpointInfo.IErrors<any>, IEndpointInfo.IQuery<AnyObject> {
|
|
11
8
|
}
|
|
12
9
|
export declare namespace IEndpointInfo {
|
|
10
|
+
export type Base = {
|
|
11
|
+
/** HTTP method of the endpoint, defaults to `GET`. See {@link EndpointMethods} */
|
|
12
|
+
readonly method: EndpointMethods;
|
|
13
|
+
/** Optional display name, for logging purposes. */
|
|
14
|
+
readonly displayName?: string;
|
|
15
|
+
};
|
|
13
16
|
export interface IIn<TIn extends object | null> {
|
|
17
|
+
/** Marker for defining input type. */
|
|
14
18
|
readonly in?: TIn;
|
|
15
19
|
}
|
|
16
|
-
export
|
|
20
|
+
export interface IOut<TOut> {
|
|
21
|
+
/** Marker for defining output type. */
|
|
17
22
|
readonly out?: TOut;
|
|
18
23
|
}
|
|
19
24
|
export interface IPath<TPath extends readonly string[]> {
|
|
25
|
+
/**
|
|
26
|
+
* Endpoint path, which can be static (just a string) or parametrized.
|
|
27
|
+
*
|
|
28
|
+
* See {@link Path}
|
|
29
|
+
*/
|
|
20
30
|
readonly path: Path.SwitchBuilder<TPath>;
|
|
21
31
|
}
|
|
22
32
|
export type QueryKeysType = boolean | string | string[] | number | number[];
|
|
@@ -24,13 +34,14 @@ export declare namespace IEndpointInfo {
|
|
|
24
34
|
export interface IQuery<TQuery extends object> {
|
|
25
35
|
/** actual query keys to be used in argument object parsing */
|
|
26
36
|
readonly queryKeys?: (string & keyof TQuery)[];
|
|
27
|
-
/**
|
|
37
|
+
/** Marker type for defining query shape. */
|
|
28
38
|
readonly queryTemplate?: TQuery;
|
|
29
39
|
}
|
|
30
40
|
export interface IErrors<TErrors> {
|
|
31
41
|
readonly errorProcessor?: (err: TErrors) => void;
|
|
32
42
|
}
|
|
33
43
|
export interface IHeaders<THeaders> {
|
|
44
|
+
/** Marker type for defining endpoint extra headers. */
|
|
34
45
|
readonly headers?: THeaders;
|
|
35
46
|
}
|
|
36
47
|
type Any = AnyObject;
|
package/types/api/error.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { StatusCodes } from './statusCodes.js';
|
|
2
|
+
/** Base DTO for getting a error response */
|
|
2
3
|
export type ApiErrorResponse<TCause = never, TErrors = number | string> = {
|
|
3
4
|
code?: TErrors;
|
|
4
5
|
message?: string;
|
|
@@ -8,6 +9,7 @@ export type ApiErrorResponse<TCause = never, TErrors = number | string> = {
|
|
|
8
9
|
export declare namespace ApiErrorResponse {
|
|
9
10
|
function create<TCause = never, TErrors = number | string>(code: TErrors, message?: string, cause?: TCause): ApiErrorResponse<TCause, TErrors>;
|
|
10
11
|
}
|
|
12
|
+
/** An Error to be thrown as an API error, and be caught */
|
|
11
13
|
export declare class ApiError<TCause = unknown, TCodes extends number = StatusCodes, TErrors extends number | string = number | string> extends Error {
|
|
12
14
|
readonly status: TCodes;
|
|
13
15
|
readonly code: TErrors;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ApiEndpoint, IEndpointInfo } from '../endpoint.js';
|
|
2
|
+
/**
|
|
3
|
+
* Request Content-Type extension for endpoint.
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
export interface IEndpointInputContentType {
|
|
7
|
+
/** Returns if endpoint is marked as form. */
|
|
8
|
+
readonly contentType?: string;
|
|
9
|
+
/** Marks this endpoint with Content-Type header to be set as 'application/x-www-form-urlencoded'. */
|
|
10
|
+
asUrlEncoded(): this;
|
|
11
|
+
/** Marks this endpoint with Content-Type header to be set as 'multipart/form-data'. */
|
|
12
|
+
asMultipartForm(): this;
|
|
13
|
+
/** Marks this endpoint with Content-Type header to be set as 'application/json'. */
|
|
14
|
+
asJson(): this;
|
|
15
|
+
/** Marks this endpoint with Content-Type header to be set as passed value. */
|
|
16
|
+
withContentType(contentType: string): this;
|
|
17
|
+
}
|
|
18
|
+
export declare namespace IEndpointInputContentType {
|
|
19
|
+
const extender: ApiEndpoint.IBuilderExtender<IEndpointInputContentType>;
|
|
20
|
+
function guard(api: IEndpointInfo): api is (IEndpointInfo & IEndpointInputContentType);
|
|
21
|
+
function tryApplyContentType(api: IEndpointInfo, headers: Record<string, string>): void;
|
|
22
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ApiEndpoint } from '../endpoint.js';
|
|
2
|
+
import type { IEndpointInfo } from '../endpoint.types.js';
|
|
3
|
+
export interface IEndpointInputValidation {
|
|
4
|
+
readonly validate?: IEndpointInputValidation.Validator<IEndpointInfo.ExtractIn<this>>;
|
|
5
|
+
withValidation(validator: IEndpointInputValidation.Validator<IEndpointInfo.ExtractIn<this>>): this;
|
|
6
|
+
}
|
|
7
|
+
export declare namespace IEndpointInputValidation {
|
|
8
|
+
type Validator<TIn> = (input: TIn) => Promise<void> | void;
|
|
9
|
+
const extender: ApiEndpoint.IBuilderExtender<IEndpointInputValidation>;
|
|
10
|
+
function guard(api: IEndpointInfo): api is (IEndpointInfo & IEndpointInputValidation);
|
|
11
|
+
function tryValidate(api: IEndpointInfo, input: IEndpointInfo.ExtractIn<IEndpointInfo>): void | Promise<void>;
|
|
12
|
+
}
|
package/types/api/helpers.d.ts
CHANGED
|
@@ -4,6 +4,6 @@ export declare const DefaultSettings: {
|
|
|
4
4
|
basePrefix: string;
|
|
5
5
|
};
|
|
6
6
|
export declare function setDefaults(settings: Partial<typeof DefaultSettings>): void;
|
|
7
|
-
export declare function getPath<T extends IEndpointInfo>(endpoint: T, pathArgs: IEndpointInfo.ExtractPath<T>, prefix?: string | boolean): string;
|
|
7
|
+
export declare function getPath<T extends IEndpointInfo.IPath<K>, K extends readonly string[]>(endpoint: T, pathArgs: IEndpointInfo.ExtractPath<T>, prefix?: string | boolean): string;
|
|
8
8
|
export declare function getTemplate<T extends IEndpointInfo>(endpoint: T, prefix?: string | boolean): string;
|
|
9
9
|
export declare function getFormattedDisplayName(endpoint: IEndpointInfo): string;
|
package/types/api/logging.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ILogger } from '../logger/abstractions.js';
|
|
2
|
+
import type { Nullable } from '../types/index.js';
|
|
3
|
+
import type { RequestConfigDetails } from './call.js';
|
|
4
|
+
/** Describes the way a request is logged.
|
|
5
|
+
*
|
|
6
|
+
* Useful for granular control over logging, especially in production.
|
|
7
|
+
*/
|
|
2
8
|
export type LogTypes<TIn = any, TOut = any> = boolean | 'full' | LogTypes.Dir | {
|
|
3
9
|
req?: boolean | LogTypes.LogFn<TIn>;
|
|
4
10
|
res?: boolean | LogTypes.LogFn<TOut>;
|
|
@@ -6,8 +12,28 @@ export type LogTypes<TIn = any, TOut = any> = boolean | 'full' | LogTypes.Dir |
|
|
|
6
12
|
export declare namespace LogTypes {
|
|
7
13
|
type Dir = 'req' | 'res';
|
|
8
14
|
type LogFn<T = any> = (data: T) => void | string | any[];
|
|
15
|
+
/**
|
|
16
|
+
* Get the enabled state of a log type.
|
|
17
|
+
* @param type Log type to check.
|
|
18
|
+
* @param dir Direction of log, either request or response.
|
|
19
|
+
*/
|
|
9
20
|
function getIsEnabled<T = unknown>(type: Nullable<LogTypes>, dir: Dir): {
|
|
10
21
|
enabled: boolean;
|
|
11
22
|
formatter?: Nullable<(data: T) => unknown>;
|
|
12
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* An example implementation for logging logic (overload for a request with no data).
|
|
26
|
+
*
|
|
27
|
+
* See the other overload for more details.
|
|
28
|
+
*/
|
|
29
|
+
function logCall(logger: ILogger, cfg: RequestConfigDetails, dir: 'req'): void;
|
|
30
|
+
/**
|
|
31
|
+
* An example implementation for logging logic (overload for a request with data).
|
|
32
|
+
*
|
|
33
|
+
* * Skips logging if the request is a GET and has no data.
|
|
34
|
+
* * Checks if logging is enabled for the given direction.
|
|
35
|
+
* * Formats the data if a custom formatter is provided.
|
|
36
|
+
* * Logs the data via specified logger.
|
|
37
|
+
*/
|
|
38
|
+
function logCall(logger: ILogger, cfg: RequestConfigDetails, dir: 'res', data: unknown): void;
|
|
13
39
|
}
|