@radatek/microserver
Version:
HTTP MicroServer
866 lines (864 loc) • 32 kB
TypeScript
/**
* MicroServer
* @version 2.3.11
* @package @radatek/microserver
* @copyright Darius Kisonas 2022
* @license MIT
*/
import http from 'http';
import net from 'net';
import { Readable } from 'stream';
import fs from 'fs';
import { EventEmitter } from 'events';
export declare class Warning extends Error {
constructor(text: string);
}
export declare class ResponseError extends Error {
static getStatusCode(text: string | number | undefined): number;
static getStatusText(text: string | number | undefined): string;
statusCode: number;
constructor(text: string | number | undefined, statusCode?: number);
}
export declare class AccessDenied extends ResponseError {
constructor(text?: string);
}
export declare class InvalidData extends ResponseError {
constructor(text?: string, type?: string);
}
export declare class NotFound extends ResponseError {
constructor(text?: string);
}
export declare class WebSocketError extends Error {
statusCode: number;
constructor(text?: string, code?: number);
}
export type Routes = () => Record<string, Array<any>> | Array<Array<any>>;
export declare abstract class Plugin {
name?: string;
priority?: number;
handler?(req: ServerRequest, res: ServerResponse, next: Function): Promise<string | object | void> | string | object | void;
routes?(): Promise<Routes> | Routes;
initialise?(): Promise<void> | void;
constructor(router: Router, ...args: any);
}
interface PluginClass {
new (router: Router, ...args: any): Plugin;
}
export type ServerRequestBody<T = any> = T extends Model<infer U extends ModelSchema> ? ModelDocument<U> : Record<string, any>;
/** Extended http.IncomingMessage */
export declare class ServerRequest<T = any> extends http.IncomingMessage {
/** Request protocol: http or https */
protocol: string;
/** Request client IP */
ip?: string;
/** Request from local network */
localip?: boolean;
/** Request is secure (https) */
secure?: boolean;
/** Request whole path */
path: string;
/** Request pathname */
pathname: string;
/** Base url */
baseUrl: string;
/** Original url */
originalUrl?: string;
/** Query parameters */
query: Record<string, string>;
/** Router named parameters */
params: Record<string, string>;
/** Router named parameters list */
paramsList: string[];
/** Router */
router: Router;
/** Authentication object */
auth?: Auth;
/** Authenticated user info */
user?: UserInfo;
/** Model used for request */
model?: T;
/** Authentication token id */
tokenId?: string;
/** Request raw body */
rawBody: Buffer[];
/** Request raw body size */
rawBodySize: number;
private constructor();
/** Update request url */
updateUrl(url: string): void;
/** Rewrite request url */
rewrite(url: string): void;
/** Request body: JSON or POST parameters */
get body(): ServerRequestBody<T>;
/** Alias to body */
get post(): ServerRequestBody<T>;
/** Get websocket */
get websocket(): WebSocket;
/** get files list in request */
files(): Promise<any[] | undefined>;
/** Decode request body */
bodyDecode(res: ServerResponse, options: any, next: () => void): void;
}
/** Extends http.ServerResponse */
export declare class ServerResponse<T = any> extends http.ServerResponse {
req: ServerRequest<T>;
router: Router;
isJson: boolean;
headersOnly: boolean;
private constructor();
/** Send error reponse */
error(error: string | number | Error): void;
/** Sets Content-Type acording to data and sends response */
send(data?: string | Buffer | Error | Readable | object): void;
/** Send json response */
json(data: any): void;
/** Send json response in form { success: false, error: err } */
jsonError(error: string | number | object | Error): void;
/** Send json response in form { success: true, ... } */
jsonSuccess(data?: object | string): void;
/** Send redirect response to specified URL with optional status code (default: 302) */
redirect(code: number | string, url?: string): void;
/** Set status code */
status(code: number): this;
file(path: string, filename?: string): void;
}
/** WebSocket options */
export interface WebSocketOptions {
maxPayload?: number;
autoPong?: boolean;
permessageDeflate?: boolean;
maxWindowBits?: number;
timeout?: number;
deflate?: boolean;
}
/** WebSocket class */
export declare class WebSocket extends EventEmitter {
ready: boolean;
constructor(req: ServerRequest, options?: WebSocketOptions);
/** Close connection */
close(reason?: number, data?: Buffer): void;
/** Generate WebSocket frame from data */
static getFrame(data: number | string | Buffer | undefined, options?: any): Buffer;
/** Send data */
send(data: string | Buffer): void;
/** Send ping frame */
ping(buffer?: Buffer): void;
/** Send pong frame */
pong(buffer?: Buffer): void;
protected _sendFrame(opcode: number, data: Buffer, cb?: () => void): void;
}
/**
* Controller for dynamic routes
*
* @example
* ```js
* class MyController extends Controller {
* static model = MyModel;
* static acl = 'auth';
*
* static 'acl:index' = '';
* static 'url:index' = 'GET /index';
* async index (req, res) {
* res.send('Hello World')
* }
*
* //function name prefixes translated to HTTP methods:
* // all => GET, get => GET, insert => POST, post => POST,
* // update => PUT, put => PUT, delete => DELETE,
* // modify => PATCH, patch => PATCH,
* // websocket => internal WebSocket
* // automatic acl will be: class_name + '/' + function_name_prefix
* // automatic url will be: method + ' /' + class_name + '/' + function_name_without_prefix
*
* //static 'acl:allUsers' = 'MyController/all';
* //static 'url:allUsers' = 'GET /MyController/Users';
* async allUsers () {
* return ['usr1', 'usr2', 'usr3']
* }
*
* //static 'acl:getOrder' = 'MyController/get';
* //static 'url:getOrder' = 'GET /Users/:id/:id1';
* static 'group:getOrder' = 'orders';
* static 'model:getOrder' = OrderModel;
* async getOrder (id: string, id1: string) {
* return {id, extras: id1, type: 'order'}
* }
*
* //static 'acl:insertOrder' = 'MyController/insert';
* //static 'url:insertOrder' = 'POST /Users/:id';
* static 'model:insertOrder' = OrderModel;
* async insertOrder (id: string, id1: string) {
* return {id, extras: id1, type: 'order'}
* }
*
* static 'acl:POST /login' = '';
* async 'POST /login' () {
* return {id, extras: id1, type: 'order'}
* }
* }
* ```
*/
export declare class Controller<T extends Model<any> = any> {
req: ServerRequest<T>;
res: ServerResponse<T>;
get model(): T | undefined;
constructor(req: ServerRequest<T>, res: ServerResponse<T>);
/** Generate routes for this controller */
static routes(): any[];
}
/** Middleware */
export interface Middleware {
(req: ServerRequest, res: ServerResponse, next: Function): any;
/** @default 0 */
priority?: number;
plugin?: Plugin;
}
declare class Waiter {
isBusy(id?: string): boolean;
startJob(id?: string): void;
endJob(id?: string): void;
get nextId(): string;
wait(id?: string): Promise<void>;
}
/** Router */
export declare class Router extends EventEmitter {
server: MicroServer;
auth?: Auth;
plugins: Record<string, Plugin>;
_waiter: Waiter;
/** @param {MicroServer} server */
constructor(server: MicroServer);
/** bind middleware or create one from string like: 'redirect:302,https://redirect.to', 'error:422', 'param:name=value', 'acl:users/get', 'model:User', 'group:Users', 'user:admin' */
bind(fn: string | Function | object): Function;
/** Handler */
handler(req: ServerRequest, res: ServerResponse, next: Function, method?: string): void;
/** Clear routes and middlewares */
clear(): this;
/**
* Add middleware route.
* Middlewares may return promises for res.jsonSuccess(...), throw errors for res.error(...), return string or {} for res.send(...)
*
* @signature add(plugin: Plugin)
* @param {Plugin} plugin plugin module instance
* @return {Promise<>}
*
* @signature add(pluginid: string, ...args: any)
* @param {string} pluginid pluginid module
* @param {...any} args arguments passed to constructor
* @return {Promise<>}
*
* @signature add(pluginClass: typeof Plugin, ...args: any)
* @param {typeof Plugin} pluginClass plugin class
* @param {...any} args arguments passed to constructor
* @return {Promise<>}
*
* @signature add(middleware: Middleware)
* @param {Middleware} middleware
* @return {Promise<>}
*
* @signature add(methodUrl: string, ...middlewares: any)
* @param {string} methodUrl 'METHOD /url' or '/url'
* @param {...any} middlewares
* @return {Promise<>}
*
* @signature add(methodUrl: string, controllerClass: typeof Controller)
* @param {string} methodUrl 'METHOD /url' or '/url'
* @param {typeof Controller} controllerClass
* @return {Promise<>}
*
* @signature add(methodUrl: string, routes: Array<Array<any>>)
* @param {string} methodUrl 'METHOD /url' or '/url'
* @param {Array<Array<any>>} routes list with subroutes: ['METHOD /suburl', ...middlewares]
* @return {Promise<>}
*
* @signature add(methodUrl: string, routes: Array<Array<any>>)
* @param {string} methodUrl 'METHOD /url' or '/url'
* @param {Array<Array<any>>} routes list with subroutes: ['METHOD /suburl', ...middlewares]
* @return {Promise<>}
*
* @signature add(routes: { [key: string]: Array<any> })
* @param { {[key: string]: Array<any>} } routes list with subroutes: 'METHOD /suburl': [...middlewares]
* @return {Promise<>}
*
* @signature add(methodUrl: string, routes: { [key: string]: Array<any> })
* @param {string} methodUrl 'METHOD /url' or '/url'
* @param { {[key: string]: Array<any>} } routes list with subroutes: 'METHOD /suburl': [...middlewares]
* @return {Promise<>}
*/
use(...args: any): Promise<void>;
waitPlugin(id: string): Promise<Plugin>;
/** Add hook */
hook(url: string, ...mid: Middleware[]): void;
/** Check if middleware allready added */
has(mid: Middleware): boolean;
}
export interface HttpHandler {
(req: ServerRequest, res: ServerResponse): void;
}
export interface TcpHandler {
(socket: net.Socket): void;
}
export interface ListenConfig {
/** listen port(s) with optional protocol and host (Ex. 8080 or '0.0.0.0:8080,8180' or 'https://0.0.0.0:8080' or 'tcp://0.0.0.0:8080' or 'tls://0.0.0.0:8080') */
listen?: string | number;
/** tls options */
tls?: {
cert: string;
key: string;
ca?: string;
};
/** custom handler */
handler?: HttpHandler | TcpHandler;
}
export interface CorsOptions {
/** allowed origins (default: '*') */
origin: string;
/** allowed headers (default: '*') */
headers: string;
/** allow credentials (default: false) */
credentials: boolean;
/** Expose headers */
expose?: string;
/** Max age */
maxAge?: number;
}
/** MicroServer configuration */
export interface MicroServerConfig extends ListenConfig {
/** server instance root path */
root?: string;
/** Auth options */
auth?: AuthOptions;
/** routes to add */
routes?: any;
/** Static file options */
static?: StaticOptions;
/** max body size (default: 5MB) */
maxBodySize?: number;
/** allowed HTTP methods */
methods?: string;
/** trust proxy */
trustProxy?: string[];
/** cors options */
cors?: string | CorsOptions | boolean;
/** upload dir (default: './upload') */
uploadDir?: string;
/** allow websocket deflate compression (default: false) */
websocketCompress?: boolean;
/** max websocket payload (default: 1MB) */
websocketMaxPayload?: number;
/** websocket max window bits 8-15 for deflate (default: 10) */
websocketMaxWindowBits?: number;
/** extra options for plugins */
[]: any;
}
export declare class MicroServer extends EventEmitter {
/** server configuration */
config: MicroServerConfig;
/** main router */
router: Router;
/** virtual host routers */
vhosts?: {
[]: Router;
};
/** all sockets */
sockets: Set<net.Socket>;
/** server instances */
servers: Set<net.Server>;
_waiter: Waiter;
static plugins: {
[]: PluginClass;
};
constructor(config: MicroServerConfig);
/** Add one time listener or call immediatelly for 'ready' */
once(name: string, cb: Function): this;
/** Add listener and call immediatelly for 'ready' */
on(name: string, cb: Function): this;
get isReady(): boolean;
waitReady(): Promise<void>;
waitPlugin(id: string): Promise<void>;
/** Listen server, should be used only if config.listen is not set */
listen(config?: ListenConfig): Promise<void>;
/** Add middleware, routes, etc.. see {router.use} */
use(...args: any): Promise<void>;
/** Default server handler */
handler(req: ServerRequest, res: ServerResponse): void;
protected requestInit(req: ServerRequest, res?: ServerResponse): void;
/** Preprocess request, used by {MicroServer.handler} */
handlerInit(req: ServerRequest, res: ServerResponse, next: Function): void;
/** Last request handler */
handlerLast(req: ServerRequest, res: ServerResponse, next?: Function): any;
/** Default upgrade handler, used for WebSockets */
handlerUpgrade(req: ServerRequest, socket: net.Socket, head: any): void;
/** Close server instance */
close(): Promise<void>;
/** Add route, alias to `server.router.use(url, ...args)` */
all(url: string, ...args: any): MicroServer;
/** Add route, alias to `server.router.use('GET ' + url, ...args)` */
get(url: string, ...args: any): MicroServer;
/** Add route, alias to `server.router.use('POST ' + url, ...args)` */
post(url: string, ...args: any): MicroServer;
/** Add route, alias to `server.router.use('PUT ' + url, ...args)` */
put(url: string, ...args: any): MicroServer;
/** Add route, alias to `server.router.use('PATCH ' + url, ...args)` */
patch(url: string, ...args: any): MicroServer;
/** Add route, alias to `server.router.use('DELETE ' + url, ...args)` */
delete(url: string, ...args: any): MicroServer;
/** Add websocket handler, alias to `server.router.use('WEBSOCKET ' + url, ...args)` */
websocket(url: string, ...args: any): MicroServer;
/** Add router hook, alias to `server.router.hook(url, ...args)` */
hook(url: string, ...args: any): MicroServer;
}
/** Static files options */
export interface StaticOptions {
/** files root directory */
root?: string;
/** url path */
path?: string;
/** additional mime types */
mimeTypes?: {
[]: string;
};
/** file extension handlers */
handlers?: {
[]: Middleware;
};
/** ignore prefixes */
ignore?: string[];
/** index file. default: 'index.html' */
index?: string;
/** Update Last-Modified header. default: true */
lastModified?: boolean;
/** Update ETag header. default: true */
etag?: boolean;
/** Max file age in seconds */
maxAge?: number;
}
export interface ServeFileOptions {
/** path */
path: string;
/** root */
root?: string;
/** file name */
filename?: string;
/** file mime type */
mimeType?: string;
/** last modified date */
lastModified?: boolean;
/** etag */
etag?: boolean;
/** max age */
maxAge?: number;
/** range */
range?: boolean;
/** stat */
stats?: fs.Stats;
}
/** Proxy plugin options */
export interface ProxyPluginOptions {
/** Base path */
path?: string;
/** Remote url */
remote?: string;
/** Match regex filter */
match?: string;
/** Override/set headers for remote */
headers?: {
[]: string;
};
/** Valid headers to forward */
validHeaders?: {
[]: boolean;
};
}
export declare class ProxyPlugin extends Plugin {
/** Default valid headers */
static validHeaders: {
[]: boolean;
};
/** Current valid headers */
validHeaders: {
[]: boolean;
};
/** Override headers to forward to remote */
headers: {
[]: string;
} | undefined;
/** Remote url */
remoteUrl: URL;
/** Match regex filter */
regex?: RegExp;
constructor(router: Router, options?: ProxyPluginOptions | string);
/** Default proxy handler */
proxyHandler(req: ServerRequest, res: ServerResponse, next: Function): any;
/** Proxy plugin handler as middleware */
handler?(req: ServerRequest, res: ServerResponse, next: Function): void;
}
/** User info */
export interface UserInfo {
/** User _id */
_id?: string;
/** User id */
id?: string;
/** User password plain or hash */
password?: string;
/** ACL options */
acl?: {
[]: boolean;
};
/** User group */
group?: string;
/** Custom user data */
[]: any;
}
/** Authentication options */
export interface AuthOptions {
/** Authentication token */
token: string | Buffer;
/** Users */
users?: {
[]: UserInfo;
} | ((usr: string, psw?: string) => Promise<UserInfo | undefined>);
/** Default ACL */
defaultAcl?: {
[]: boolean;
};
/** Expire time in seconds */
expire?: number;
/** Authentication mode */
mode?: 'cookie' | 'token';
/** Authentication realm for basic authentication */
realm?: string;
/** Redirect URL */
redirect?: string;
/** Authentication cache */
cache?: {
[]: {
data: UserInfo;
time: number;
};
};
/** Interal next cache cleanup time */
cacheCleanup?: number;
}
export interface AuthOptionsInternal extends AuthOptions {
/** Authentication token */
token: Buffer;
/** Users */
users: (usr: string, psw?: string) => Promise<UserInfo | undefined>;
/** Default ACL */
defaultAcl: {
[]: boolean;
};
/** Expire time in seconds */
expire: number;
/** Use object token instead of user id */
objectToken: boolean;
/** Authentication mode */
mode: 'cookie' | 'token';
/** Authentication realm for basic authentication */
realm: string;
/** Redirect URL */
redirect: string;
/** Authentication cache */
cache: {
[]: {
data: UserInfo;
time: number;
};
};
/** Interal next cache cleanup time */
cacheCleanup: number;
}
/** Authentication class */
export declare class Auth {
/** Server request */
req: ServerRequest | undefined;
/** Server response */
res: ServerResponse | undefined;
/** Authentication options */
options: AuthOptionsInternal;
constructor(options: AuthOptionsInternal, req?: ServerRequest, res?: ServerResponse);
/** Decode token */
decode(data: string): {
data: string;
expire: number;
};
/** Encode token */
encode(data: string, expire?: number): string;
/**
* Check acl over authenticated user with: `id`, `group/*`, `*`
* @param {string} id - to authenticate: `id`, `group/id`, `model/action`, comma separated best: true => false => def
* @param {boolean} [def=false] - default access
*/
acl(id: string, def?: boolean): boolean;
/**
* Authenticate user and setup cookie
* @param {string|UserInfo} usr - user id used with options.users to retrieve user object. User object must contain `id` and `acl` object (Ex. usr = {id:'usr', acl:{'users/*':true}})
* @param {string} [psw] - user password (if used for user authentication with options.users)
* @param {number} [expire] - expire time in seconds (default: options.expire)
*/
token(usr: string | UserInfo | undefined, psw: string | undefined, expire?: number): Promise<string | undefined>;
/**
* Authenticate user and setup cookie
*/
login(usr: string | UserInfo | undefined, psw?: string, options?: {
expire?: number;
salt?: string;
}): Promise<UserInfo | undefined>;
/** Logout logged in user */
logout(): void;
/** Get hashed string from user and password */
password(usr: string, psw: string, salt?: string): string;
/** Get hashed string from user and password */
static password(usr: string, psw: string, salt?: string): string;
/** Validate user password */
checkPassword(usr: string, psw: string, storedPsw: string, salt?: string): boolean;
/** Validate user password */
static checkPassword(usr: string, psw: string, storedPsw: string, salt?: string): boolean;
/** Clear user cache if users setting where changed */
clearCache(): void;
}
/** Create microserver */
export declare function create(config: MicroServerConfig): MicroServer;
export interface FileStoreOptions {
/** Base directory */
dir?: string;
/** Cache timeout in milliseconds */
cacheTimeout?: number;
/** Max number of cached items */
cacheItems?: number;
/** Debounce timeout in milliseconds for autosave */
debounceTimeout?: number;
}
/** JSON File store */
export declare class FileStore {
constructor(options?: FileStoreOptions);
/** cleanup cache */
cleanup(): void;
close(): Promise<void>;
sync(): Promise<void>;
/** load json file data */
load(name: string, autosave?: boolean): Promise<any>;
/** save data */
save(name: string, data: any): Promise<any>;
/** load all files in directory */
all(name: string, autosave?: boolean): Promise<Record<string, any>>;
/** delete data file */
delete(name: string): Promise<void>;
/** Observe data object */
observe(data: object, cb: (data: object, key: string, value: any) => void): object;
}
/** Model validation options */
interface ModelContextOptions {
/** User info */
user?: UserInfo;
/** Request params */
params?: Object;
/** is insert */
insert?: boolean;
/** is read-only */
readOnly?: boolean;
/** validate */
validate?: boolean;
/** use default */
default?: boolean;
/** use primary key only for filter */
primaryKey?: boolean;
/** projection fields */
projection?: Record<string, 0 | 1 | true | false>;
}
/** Model field validation options */
interface ModelValidateFieldOptions extends ModelContextOptions {
name: string;
field: ResolvedFieldSchema;
model: Model<any>;
}
export interface ModelCallbackFunc {
(options: any): any;
}
type ModelBasicCtorType = typeof String | typeof Number | typeof Boolean | typeof Date;
type ModelBasicNamedType = 'string' | 'String' | 'number' | 'Number' | 'int' | 'Int' | 'integer' | 'Integer' | 'boolean' | 'Boolean' | 'object' | 'Object' | 'objectid' | 'ObjectId' | 'date' | 'Date' | 'any' | 'Any';
type ModelBasicType = ModelBasicNamedType | ModelBasicCtorType | Model<any>;
type ModelFieldSimpleType = ModelBasicType | [ModelBasicType] | [ModelBasicType, ...never[]];
/** Model field description */
export interface ModelFieldSchema {
/** Field type */
type: ModelFieldSimpleType;
/** Is array */
array?: true | false;
/** Is primary key, used for filtering */
primaryKey?: true | false;
/** Is required */
required?: boolean | string | ModelCallbackFunc;
/** Can read */
canRead?: boolean | string | ModelCallbackFunc;
/** Can write */
canWrite?: boolean | string | ModelCallbackFunc;
/** Default value */
default?: number | string | ModelCallbackFunc;
/** Validate function */
validate?: (value: any, options: ModelContextOptions) => string | number | object | null | Error | typeof Error;
/** Valid values */
enum?: Array<string | number>;
/** Minimum value for string and number */
minimum?: number | string;
/** Maximum value for string and number */
maximum?: number | string;
/** Regex validation or 'email', 'url', 'date', 'time', 'date-time' */
format?: string;
}
interface ResolvedFieldSchema {
type: string;
model?: Model<any>;
primaryKey?: boolean;
required?: ModelCallbackFunc;
canRead: ModelCallbackFunc;
canWrite: ModelCallbackFunc;
default: ModelCallbackFunc;
validate: (value: any, options: ModelValidateFieldOptions) => any;
}
export interface ModelSchema {
[]: ModelFieldSchema | ModelFieldSimpleType;
}
type ModelDocumentTypeByName<T> = T extends 'string' | 'String' ? string : T extends 'number' | 'Number' | 'int' | 'Int' ? number : T extends 'boolean' | 'Boolean' ? boolean : T extends 'date' | 'Date' ? Date : T extends 'objectid' | 'ObjectId' ? string : T extends 'object' | 'Object' ? Record<string, any> : T extends 'any' | 'Any' ? any : never;
type ModelDocumentTypeByCtor<T> = T extends typeof String ? string : T extends typeof Number ? number : T extends typeof Boolean ? boolean : T extends typeof Date ? Date : T extends typeof Object ? Record<string, any> : never;
type ModelFieldTypeExtract<T extends ModelFieldSchema> = T['type'] extends string ? ModelDocumentTypeByName<T['type']> : T['type'] extends Array<infer U> ? Array<ModelDocumentType<U>> : T['type'] extends Function ? ModelDocumentTypeByCtor<T['type']> : T['type'] extends Model<infer U> ? ModelDocument<U> : never;
type ModelDocumentType<T> = T extends string ? ModelDocumentTypeByName<T> : T extends Array<infer U> ? Array<ModelDocumentType<U>> : T extends Function ? ModelDocumentTypeByCtor<T> : T extends ModelFieldSchema ? ModelFieldTypeExtract<T> : T extends Model<infer U> ? ModelDocument<U> : never;
export interface ModelDocumentField<T extends ModelFieldSchema> {
value: T['array'] extends true ? Array<ModelFieldTypeExtract<T>> : ModelFieldTypeExtract<T>;
required?: T['required'];
canRead?: T['canRead'];
canWrite?: T['canWrite'];
default?: T['default'];
validate?: T['validate'];
}
export type ModelDocument<T extends ModelSchema> = {
[]: ModelDocumentType<T[K]>;
} & {
_id?: string;
};
export declare interface ModelCollections {
collection(name: string): Promise<MicroCollection>;
}
export declare class Models {
}
export declare class Model<TSchema extends ModelSchema> {
static collections: ModelCollections;
static models: Models;
static set db(db: any);
static get db(): any;
/** Dynamic model extension */
static dynamic<T extends Model<any>>(model: T, options: {
controller?: Controller;
collection?: MicroCollection<any>;
req?: ServerRequest;
} | Controller): T;
static register<K extends string, T extends Model<any>>(name: K, model: T): typeof Model;
/** Define model */
static define<K extends string, T extends ModelSchema>(name: K, schema: T, options?: {
collection?: MicroCollection | Promise<MicroCollection>;
class?: typeof Model;
}): Model<T>;
/** Model fields description */
model: Record<string, ResolvedFieldSchema>;
/** Model collection for persistance */
collection?: MicroCollection | Promise<MicroCollection>;
/** Custom options */
options: Record<string, any>;
/** Create model acording to description */
constructor(schema: TSchema, options?: {
collection?: MicroCollection | Promise<MicroCollection>;
name?: string;
});
/** Get model name */
get name(): any;
/** Validate data over model */
document(data: Record<string, any>, options?: ModelContextOptions): ModelDocument<TSchema>;
/** Generate filter for data queries */
getFilter(data: Record<string, any>, options?: ModelContextOptions): Record<string, any>;
/** Find one document */
findOne(query: Query, options?: ModelContextOptions): Promise<ModelDocument<TSchema> | undefined>;
/** Find many documents */
findMany(query: Query, options?: ModelContextOptions): Promise<ModelDocument<TSchema>[]>;
/** Insert a new document */
insert(data: Record<string, any>, options?: ModelContextOptions): Promise<ModelDocument<TSchema>>;
/** Update one matching document */
update(query: Record<string, any>, options?: ModelContextOptions): Promise<ModelDocument<TSchema>>;
/** Update many matching documents */
updateMany(query: Record<string, any>, update: Record<string, any>, options?: ModelContextOptions): Promise<ModelDocument<TSchema>>;
/** Delete one matching document */
delete(query: Query, options?: ModelContextOptions): Promise<number>;
/** Delete many matching documents */
deleteMany(query: Query, options?: ModelContextOptions): Promise<number>;
/** Microserver middleware */
handler(req: ServerRequest, res: ServerResponse): any;
}
export declare interface MicroCollectionOptions<T extends ModelSchema = any> {
/** Collection name */
name?: string;
/** Custom data saver */
save?: (id: string, doc: ModelDocument<T> | undefined, col: MicroCollection) => Promise<ModelDocument<T>>;
/** Preloaded data object */
data?: Record<string, ModelDocument<T>>;
}
export declare interface Query {
[]: any;
}
/** Cursor */
export declare interface Cursor<T extends ModelSchema> {
forEach(cb: Function, self?: any): Promise<number>;
all(): Promise<ModelDocument<T>[]>;
}
/** Find options */
export declare interface FindOptions {
/** Query */
query?: Query;
/** is upsert */
upsert?: boolean;
/** is upsert */
delete?: boolean;
/** update object */
update?: Query;
/** maximum number of hits */
limit?: number;
}
/** Collection factory */
export declare class MicroCollectionStore {
constructor(dataPath?: string, storeTimeDelay?: number);
/** Get collection */
collection(name: string): Promise<MicroCollection>;
}
/** minimalistic indexed mongo type collection with persistance for usage with Model */
export declare class MicroCollection<TSchema extends ModelSchema = any> {
/** Collection name */
name: string;
/** Collection data */
data: Record<string, ModelDocument<TSchema>>;
constructor(options?: MicroCollectionOptions<TSchema>);
/** Query document with query filter */
protected queryDocument(query?: Query, data?: ModelDocument<TSchema>): ModelDocument<TSchema>;
/** Count all documents */
countDocuments(): Promise<number>;
/** Find one matching document */
findOne(query: Query): Promise<ModelDocument<TSchema> | undefined>;
/** Find all matching documents */
find(query: Query): Cursor<TSchema>;
/** Find and modify one matching document */
findAndModify(options: FindOptions): Promise<ModelDocument<TSchema> | undefined>;
/** Insert one document */
insertOne(doc: ModelDocument<TSchema>): Promise<ModelDocument<TSchema>>;
/** Insert multiple documents */
insertMany(docs: ModelDocument<TSchema>[]): Promise<ModelDocument<TSchema>[]>;
/** Delete one matching document */
deleteOne(query: Query): Promise<number>;
/** Delete all matching documents */
deleteMany(query: Query): Promise<number>;
updateOne(query: Query, update: Record<string, any>, options?: FindOptions): Promise<{
upsertedId: any;
modifiedCount: number;
}>;
updateMany(query: Query, update: Record<string, any>, options?: FindOptions): Promise<{
upsertedId: any;
modifiedCount: number;
}>;
}