import { UUID } from 'crypto'; import { z } from 'zod'; import * as express from 'express'; import { IncomingHttpHeaders } from 'http2'; import { QueryResultRow, QueryConfigValues, QueryResult, PoolConfig, Pool, ClientConfig, Client } from 'pg'; import * as pegjs from 'pegjs'; type LogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'; interface ResturaLogger { readonly level: LogLevel; fatal(msg: unknown, ...args: unknown[]): void; error(msg: unknown, ...args: unknown[]): void; warn(msg: unknown, ...args: unknown[]): void; info(msg: unknown, ...args: unknown[]): void; debug(msg: unknown, ...args: unknown[]): void; trace(msg: unknown, ...args: unknown[]): void; } declare const logger: ResturaLogger; declare function setLogger(impl: ResturaLogger): void; declare const whereDataSchema: z.ZodObject<{ tableName: z.ZodOptional; columnName: z.ZodOptional; operator: z.ZodOptional": ">"; "<=": "<="; ">=": ">="; "!=": "!="; LIKE: "LIKE"; "NOT LIKE": "NOT LIKE"; IN: "IN"; "NOT IN": "NOT IN"; "STARTS WITH": "STARTS WITH"; "ENDS WITH": "ENDS WITH"; IS: "IS"; "IS NOT": "IS NOT"; }>>; value: z.ZodOptional>; custom: z.ZodOptional; conjunction: z.ZodOptional>; }, z.core.$strict>; type WhereData = z.infer; declare const joinDataSchema: z.ZodObject<{ table: z.ZodString; localTable: z.ZodOptional; localTableAlias: z.ZodOptional; localColumnName: z.ZodOptional; foreignColumnName: z.ZodOptional; custom: z.ZodOptional; type: z.ZodEnum<{ LEFT: "LEFT"; INNER: "INNER"; RIGHT: "RIGHT"; }>; alias: z.ZodString; }, z.core.$strict>; type JoinData = z.infer; declare const responseDataSchema: z.ZodObject<{ name: z.ZodString; selector: z.ZodOptional; subquery: z.ZodOptional; localTableAlias: z.ZodOptional; localColumnName: z.ZodOptional; foreignColumnName: z.ZodOptional; custom: z.ZodOptional; type: z.ZodEnum<{ LEFT: "LEFT"; INNER: "INNER"; RIGHT: "RIGHT"; }>; alias: z.ZodString; }, z.core.$strict>>; where: z.ZodArray; columnName: z.ZodOptional; operator: z.ZodOptional": ">"; "<=": "<="; ">=": ">="; "!=": "!="; LIKE: "LIKE"; "NOT LIKE": "NOT LIKE"; IN: "IN"; "NOT IN": "NOT IN"; "STARTS WITH": "STARTS WITH"; "ENDS WITH": "ENDS WITH"; IS: "IS"; "IS NOT": "IS NOT"; }>>; value: z.ZodOptional>; custom: z.ZodOptional; conjunction: z.ZodOptional>; }, z.core.$strict>>; properties: z.ZodArray>; groupBy: z.ZodOptional>; orderBy: z.ZodOptional; tableName: z.ZodString; }, z.core.$strict>>; }, z.core.$strip>>; type: z.ZodOptional; }, z.core.$strict>; type ResponseData = z.infer; declare const standardRouteSchema: z.ZodObject<{ method: z.ZodEnum<{ GET: "GET"; POST: "POST"; PUT: "PUT"; PATCH: "PATCH"; DELETE: "DELETE"; }>; name: z.ZodString; description: z.ZodString; path: z.ZodString; deprecation: z.ZodOptional; }, z.core.$strip>>; roles: z.ZodArray; scopes: z.ZodArray; type: z.ZodEnum<{ ONE: "ONE"; ARRAY: "ARRAY"; PAGED: "PAGED"; }>; table: z.ZodString; joins: z.ZodArray; localTableAlias: z.ZodOptional; localColumnName: z.ZodOptional; foreignColumnName: z.ZodOptional; custom: z.ZodOptional; type: z.ZodEnum<{ LEFT: "LEFT"; INNER: "INNER"; RIGHT: "RIGHT"; }>; alias: z.ZodString; }, z.core.$strict>>; assignments: z.ZodArray>; where: z.ZodArray; columnName: z.ZodOptional; operator: z.ZodOptional": ">"; "<=": "<="; ">=": ">="; "!=": "!="; LIKE: "LIKE"; "NOT LIKE": "NOT LIKE"; IN: "IN"; "NOT IN": "NOT IN"; "STARTS WITH": "STARTS WITH"; "ENDS WITH": "ENDS WITH"; IS: "IS"; "IS NOT": "IS NOT"; }>>; value: z.ZodOptional>; custom: z.ZodOptional; conjunction: z.ZodOptional>; }, z.core.$strict>>; request: z.ZodArray; validator: z.ZodArray; value: z.ZodUnion, z.ZodNumber, z.ZodArray]>; }, z.core.$strict>>; }, z.core.$strict>>; response: z.ZodArray; subquery: z.ZodOptional; localTableAlias: z.ZodOptional; localColumnName: z.ZodOptional; foreignColumnName: z.ZodOptional; custom: z.ZodOptional; type: z.ZodEnum<{ LEFT: "LEFT"; INNER: "INNER"; RIGHT: "RIGHT"; }>; alias: z.ZodString; }, z.core.$strict>>; where: z.ZodArray; columnName: z.ZodOptional; operator: z.ZodOptional": ">"; "<=": "<="; ">=": ">="; "!=": "!="; LIKE: "LIKE"; "NOT LIKE": "NOT LIKE"; IN: "IN"; "NOT IN": "NOT IN"; "STARTS WITH": "STARTS WITH"; "ENDS WITH": "ENDS WITH"; IS: "IS"; "IS NOT": "IS NOT"; }>>; value: z.ZodOptional>; custom: z.ZodOptional; conjunction: z.ZodOptional>; }, z.core.$strict>>; properties: z.ZodArray>; groupBy: z.ZodOptional>; orderBy: z.ZodOptional; tableName: z.ZodString; }, z.core.$strict>>; }, z.core.$strip>>; type: z.ZodOptional; }, z.core.$strict>>; groupBy: z.ZodOptional>; orderBy: z.ZodOptional; tableName: z.ZodString; }, z.core.$strict>>; }, z.core.$strict>; type StandardRouteData = z.infer; declare const customRouteSchema: z.ZodObject<{ method: z.ZodEnum<{ GET: "GET"; POST: "POST"; PUT: "PUT"; PATCH: "PATCH"; DELETE: "DELETE"; }>; name: z.ZodString; description: z.ZodString; path: z.ZodString; deprecation: z.ZodOptional; }, z.core.$strip>>; roles: z.ZodArray; scopes: z.ZodArray; type: z.ZodEnum<{ CUSTOM_ONE: "CUSTOM_ONE"; CUSTOM_ARRAY: "CUSTOM_ARRAY"; CUSTOM_PAGED: "CUSTOM_PAGED"; }>; responseType: z.ZodUnion]>; requestType: z.ZodOptional; request: z.ZodOptional; validator: z.ZodArray; value: z.ZodUnion, z.ZodNumber, z.ZodArray]>; }, z.core.$strict>>; }, z.core.$strict>>>; fileUploadType: z.ZodOptional>; }, z.core.$strict>; type CustomRouteData = z.infer; type RouteData = CustomRouteData | StandardRouteData; declare const columnDataSchema: z.ZodObject<{ name: z.ZodString; type: z.ZodUnion, z.ZodEnum<{ CHAR: "CHAR"; VARCHAR: "VARCHAR"; TEXT: "TEXT"; BYTEA: "BYTEA"; }>, z.ZodEnum<{ DATE: "DATE"; TIMESTAMP: "TIMESTAMP"; TIMESTAMPTZ: "TIMESTAMPTZ"; TIME: "TIME"; INTERVAL: "INTERVAL"; }>, z.ZodEnum<{ JSON: "JSON"; JSONB: "JSONB"; }>, z.ZodEnum<{ SMALLINT: "SMALLINT"; INTEGER: "INTEGER"; BIGINT: "BIGINT"; DECIMAL: "DECIMAL"; BOOLEAN: "BOOLEAN"; TINYINT: "TINYINT"; MEDIUMINT: "MEDIUMINT"; FLOAT: "FLOAT"; DOUBLE: "DOUBLE"; }>, z.ZodEnum<{ CHAR: "CHAR"; VARCHAR: "VARCHAR"; TEXT: "TEXT"; JSON: "JSON"; TINYTEXT: "TINYTEXT"; TINYBLOB: "TINYBLOB"; BLOB: "BLOB"; MEDIUMTEXT: "MEDIUMTEXT"; MEDIUMBLOB: "MEDIUMBLOB"; LONGTEXT: "LONGTEXT"; LONGBLOB: "LONGBLOB"; ENUM: "ENUM"; }>, z.ZodEnum<{ DATE: "DATE"; TIMESTAMP: "TIMESTAMP"; TIME: "TIME"; DATETIME: "DATETIME"; }>]>; isNullable: z.ZodBoolean; roles: z.ZodArray; scopes: z.ZodArray; comment: z.ZodOptional; default: z.ZodOptional; value: z.ZodOptional; isPrimary: z.ZodOptional; isUnique: z.ZodOptional; hasAutoIncrement: z.ZodOptional; length: z.ZodOptional; }, z.core.$strict>; type ColumnData = z.infer; declare const indexColumnSchema: z.ZodUnion; expression: z.ZodOptional; opclass: z.ZodOptional; }, z.core.$strict>]>; type IndexColumnData = z.infer; declare const foreignKeyDataSchema: z.ZodObject<{ name: z.ZodString; column: z.ZodOptional; columns: z.ZodOptional>; refTable: z.ZodString; refColumn: z.ZodOptional; refColumns: z.ZodOptional>; onDelete: z.ZodEnum<{ CASCADE: "CASCADE"; "SET NULL": "SET NULL"; RESTRICT: "RESTRICT"; "NO ACTION": "NO ACTION"; "SET DEFAULT": "SET DEFAULT"; }>; onUpdate: z.ZodEnum<{ CASCADE: "CASCADE"; "SET NULL": "SET NULL"; RESTRICT: "RESTRICT"; "NO ACTION": "NO ACTION"; "SET DEFAULT": "SET DEFAULT"; }>; }, z.core.$strict>; type ForeignKeyData = z.infer; declare const tableDataSchema: z.ZodObject<{ name: z.ZodString; columns: z.ZodArray, z.ZodEnum<{ CHAR: "CHAR"; VARCHAR: "VARCHAR"; TEXT: "TEXT"; BYTEA: "BYTEA"; }>, z.ZodEnum<{ DATE: "DATE"; TIMESTAMP: "TIMESTAMP"; TIMESTAMPTZ: "TIMESTAMPTZ"; TIME: "TIME"; INTERVAL: "INTERVAL"; }>, z.ZodEnum<{ JSON: "JSON"; JSONB: "JSONB"; }>, z.ZodEnum<{ SMALLINT: "SMALLINT"; INTEGER: "INTEGER"; BIGINT: "BIGINT"; DECIMAL: "DECIMAL"; BOOLEAN: "BOOLEAN"; TINYINT: "TINYINT"; MEDIUMINT: "MEDIUMINT"; FLOAT: "FLOAT"; DOUBLE: "DOUBLE"; }>, z.ZodEnum<{ CHAR: "CHAR"; VARCHAR: "VARCHAR"; TEXT: "TEXT"; JSON: "JSON"; TINYTEXT: "TINYTEXT"; TINYBLOB: "TINYBLOB"; BLOB: "BLOB"; MEDIUMTEXT: "MEDIUMTEXT"; MEDIUMBLOB: "MEDIUMBLOB"; LONGTEXT: "LONGTEXT"; LONGBLOB: "LONGBLOB"; ENUM: "ENUM"; }>, z.ZodEnum<{ DATE: "DATE"; TIMESTAMP: "TIMESTAMP"; TIME: "TIME"; DATETIME: "DATETIME"; }>]>; isNullable: z.ZodBoolean; roles: z.ZodArray; scopes: z.ZodArray; comment: z.ZodOptional; default: z.ZodOptional; value: z.ZodOptional; isPrimary: z.ZodOptional; isUnique: z.ZodOptional; hasAutoIncrement: z.ZodOptional; length: z.ZodOptional; }, z.core.$strict>>; indexes: z.ZodArray; expression: z.ZodOptional; opclass: z.ZodOptional; }, z.core.$strict>]>>; isUnique: z.ZodBoolean; isPrimaryKey: z.ZodBoolean; order: z.ZodEnum<{ ASC: "ASC"; DESC: "DESC"; }>; where: z.ZodOptional; using: z.ZodOptional>; }, z.core.$strict>>; foreignKeys: z.ZodArray; columns: z.ZodOptional>; refTable: z.ZodString; refColumn: z.ZodOptional; refColumns: z.ZodOptional>; onDelete: z.ZodEnum<{ CASCADE: "CASCADE"; "SET NULL": "SET NULL"; RESTRICT: "RESTRICT"; "NO ACTION": "NO ACTION"; "SET DEFAULT": "SET DEFAULT"; }>; onUpdate: z.ZodEnum<{ CASCADE: "CASCADE"; "SET NULL": "SET NULL"; RESTRICT: "RESTRICT"; "NO ACTION": "NO ACTION"; "SET DEFAULT": "SET DEFAULT"; }>; }, z.core.$strict>>; checkConstraints: z.ZodArray>; roles: z.ZodArray; scopes: z.ZodArray; notify: z.ZodOptional, z.ZodArray]>>; }, z.core.$strict>; type TableData = z.infer; declare const resturaSchema: z.ZodObject<{ database: z.ZodArray, z.ZodEnum<{ CHAR: "CHAR"; VARCHAR: "VARCHAR"; TEXT: "TEXT"; BYTEA: "BYTEA"; }>, z.ZodEnum<{ DATE: "DATE"; TIMESTAMP: "TIMESTAMP"; TIMESTAMPTZ: "TIMESTAMPTZ"; TIME: "TIME"; INTERVAL: "INTERVAL"; }>, z.ZodEnum<{ JSON: "JSON"; JSONB: "JSONB"; }>, z.ZodEnum<{ SMALLINT: "SMALLINT"; INTEGER: "INTEGER"; BIGINT: "BIGINT"; DECIMAL: "DECIMAL"; BOOLEAN: "BOOLEAN"; TINYINT: "TINYINT"; MEDIUMINT: "MEDIUMINT"; FLOAT: "FLOAT"; DOUBLE: "DOUBLE"; }>, z.ZodEnum<{ CHAR: "CHAR"; VARCHAR: "VARCHAR"; TEXT: "TEXT"; JSON: "JSON"; TINYTEXT: "TINYTEXT"; TINYBLOB: "TINYBLOB"; BLOB: "BLOB"; MEDIUMTEXT: "MEDIUMTEXT"; MEDIUMBLOB: "MEDIUMBLOB"; LONGTEXT: "LONGTEXT"; LONGBLOB: "LONGBLOB"; ENUM: "ENUM"; }>, z.ZodEnum<{ DATE: "DATE"; TIMESTAMP: "TIMESTAMP"; TIME: "TIME"; DATETIME: "DATETIME"; }>]>; isNullable: z.ZodBoolean; roles: z.ZodArray; scopes: z.ZodArray; comment: z.ZodOptional; default: z.ZodOptional; value: z.ZodOptional; isPrimary: z.ZodOptional; isUnique: z.ZodOptional; hasAutoIncrement: z.ZodOptional; length: z.ZodOptional; }, z.core.$strict>>; indexes: z.ZodArray; expression: z.ZodOptional; opclass: z.ZodOptional; }, z.core.$strict>]>>; isUnique: z.ZodBoolean; isPrimaryKey: z.ZodBoolean; order: z.ZodEnum<{ ASC: "ASC"; DESC: "DESC"; }>; where: z.ZodOptional; using: z.ZodOptional>; }, z.core.$strict>>; foreignKeys: z.ZodArray; columns: z.ZodOptional>; refTable: z.ZodString; refColumn: z.ZodOptional; refColumns: z.ZodOptional>; onDelete: z.ZodEnum<{ CASCADE: "CASCADE"; "SET NULL": "SET NULL"; RESTRICT: "RESTRICT"; "NO ACTION": "NO ACTION"; "SET DEFAULT": "SET DEFAULT"; }>; onUpdate: z.ZodEnum<{ CASCADE: "CASCADE"; "SET NULL": "SET NULL"; RESTRICT: "RESTRICT"; "NO ACTION": "NO ACTION"; "SET DEFAULT": "SET DEFAULT"; }>; }, z.core.$strict>>; checkConstraints: z.ZodArray>; roles: z.ZodArray; scopes: z.ZodArray; notify: z.ZodOptional, z.ZodArray]>>; }, z.core.$strict>>; endpoints: z.ZodArray; name: z.ZodString; description: z.ZodString; path: z.ZodString; deprecation: z.ZodOptional; }, z.core.$strip>>; roles: z.ZodArray; scopes: z.ZodArray; type: z.ZodEnum<{ ONE: "ONE"; ARRAY: "ARRAY"; PAGED: "PAGED"; }>; table: z.ZodString; joins: z.ZodArray; localTableAlias: z.ZodOptional; localColumnName: z.ZodOptional; foreignColumnName: z.ZodOptional; custom: z.ZodOptional; type: z.ZodEnum<{ LEFT: "LEFT"; INNER: "INNER"; RIGHT: "RIGHT"; }>; alias: z.ZodString; }, z.core.$strict>>; assignments: z.ZodArray>; where: z.ZodArray; columnName: z.ZodOptional; operator: z.ZodOptional": ">"; "<=": "<="; ">=": ">="; "!=": "!="; LIKE: "LIKE"; "NOT LIKE": "NOT LIKE"; IN: "IN"; "NOT IN": "NOT IN"; "STARTS WITH": "STARTS WITH"; "ENDS WITH": "ENDS WITH"; IS: "IS"; "IS NOT": "IS NOT"; }>>; value: z.ZodOptional>; custom: z.ZodOptional; conjunction: z.ZodOptional>; }, z.core.$strict>>; request: z.ZodArray; validator: z.ZodArray; value: z.ZodUnion, z.ZodNumber, z.ZodArray]>; }, z.core.$strict>>; }, z.core.$strict>>; response: z.ZodArray; subquery: z.ZodOptional; localTableAlias: z.ZodOptional; localColumnName: z.ZodOptional; foreignColumnName: z.ZodOptional; custom: z.ZodOptional; type: z.ZodEnum<{ LEFT: "LEFT"; INNER: "INNER"; RIGHT: "RIGHT"; }>; alias: z.ZodString; }, z.core.$strict>>; where: z.ZodArray; columnName: z.ZodOptional; operator: z.ZodOptional": ">"; "<=": "<="; ">=": ">="; "!=": "!="; LIKE: "LIKE"; "NOT LIKE": "NOT LIKE"; IN: "IN"; "NOT IN": "NOT IN"; "STARTS WITH": "STARTS WITH"; "ENDS WITH": "ENDS WITH"; IS: "IS"; "IS NOT": "IS NOT"; }>>; value: z.ZodOptional>; custom: z.ZodOptional; conjunction: z.ZodOptional>; }, z.core.$strict>>; properties: z.ZodArray>; groupBy: z.ZodOptional>; orderBy: z.ZodOptional; tableName: z.ZodString; }, z.core.$strict>>; }, z.core.$strip>>; type: z.ZodOptional; }, z.core.$strict>>; groupBy: z.ZodOptional>; orderBy: z.ZodOptional; tableName: z.ZodString; }, z.core.$strict>>; }, z.core.$strict>, z.ZodObject<{ method: z.ZodEnum<{ GET: "GET"; POST: "POST"; PUT: "PUT"; PATCH: "PATCH"; DELETE: "DELETE"; }>; name: z.ZodString; description: z.ZodString; path: z.ZodString; deprecation: z.ZodOptional; }, z.core.$strip>>; roles: z.ZodArray; scopes: z.ZodArray; type: z.ZodEnum<{ CUSTOM_ONE: "CUSTOM_ONE"; CUSTOM_ARRAY: "CUSTOM_ARRAY"; CUSTOM_PAGED: "CUSTOM_PAGED"; }>; responseType: z.ZodUnion]>; requestType: z.ZodOptional; request: z.ZodOptional; validator: z.ZodArray; value: z.ZodUnion, z.ZodNumber, z.ZodArray]>; }, z.core.$strict>>; }, z.core.$strict>>>; fileUploadType: z.ZodOptional>; }, z.core.$strict>]>>; }, z.core.$strict>>; globalParams: z.ZodArray; roles: z.ZodArray; scopes: z.ZodArray; customTypes: z.ZodArray; extensions: z.ZodOptional>; }, z.core.$strict>; type ResturaSchema = z.infer; declare function isSchemaValid(schemaToCheck: unknown): Promise; /** * @deprecated This is used for passing around until we finally get to the sending externally * // TODO: Remove once backwards compatibility is no longer needed */ interface RsErrorInternalData = Record> { err: ErrorCode; msg: string; stack: string; status: number; options?: T; } declare enum HtmlStatusCodes { BAD_REQUEST = 400, UNAUTHORIZED = 401, PAYMENT_REQUIRED = 402, FORBIDDEN = 403, NOT_FOUND = 404, METHOD_NOT_ALLOWED = 405, REQUEST_TIMEOUT = 408, CONFLICT = 409, GONE = 410, PAYLOAD_TOO_LARGE = 413, UNSUPPORTED_MEDIA_TYPE = 415, UPGRADE_REQUIRED = 426, UNPROCESSABLE_ENTITY = 422, TOO_MANY_REQUESTS = 429, SERVER_ERROR = 500, NOT_IMPLEMENTED = 501, BAD_GATEWAY = 502, SERVICE_UNAVAILABLE = 503, GATEWAY_TIMEOUT = 504, NETWORK_CONNECT_TIMEOUT = 599 } type ErrorCode = 'BAD_REQUEST' | 'UNAUTHORIZED' | 'PAYMENT_REQUIRED' | 'FORBIDDEN' | 'NOT_FOUND' | 'METHOD_NOT_ALLOWED' | 'REQUEST_TIMEOUT' | 'CONFLICT' | 'GONE' | 'PAYLOAD_TOO_LARGE' | 'UNSUPPORTED_MEDIA_TYPE' | 'UPGRADE_REQUIRED' | 'UNPROCESSABLE_ENTITY' | 'TOO_MANY_REQUESTS' | 'SERVER_ERROR' | 'NOT_IMPLEMENTED' | 'BAD_GATEWAY' | 'SERVICE_UNAVAILABLE' | 'GATEWAY_TIMEOUT' | 'NETWORK_CONNECT_TIMEOUT' | 'UNKNOWN_ERROR' | 'RATE_LIMIT_EXCEEDED' | 'INVALID_TOKEN' | 'INCORRECT_EMAIL_OR_PASSWORD' | 'DUPLICATE' | 'CONNECTION_ERROR' | 'SCHEMA_ERROR' | 'DATABASE_ERROR'; declare class RsError = Record> extends Error { err: ErrorCode; msg: string; options?: T; status?: number; constructor(errCode: ErrorCode, message?: string, options?: T); toJSON(): Record; static htmlStatus(code: ErrorCode): number; static isRsError(error: unknown): error is RsError; } interface SchemaChangeValue { name: string; changeType: 'NEW' | 'MODIFIED' | 'DELETED'; } interface SchemaPreview { commands: string; endPoints: SchemaChangeValue[]; globalParams: SchemaChangeValue[]; roles: SchemaChangeValue[]; customTypes: boolean; } type StandardOrderTypes = 'ASC' | 'DESC' | 'RAND' | 'NONE'; type ConjunctionTypes = 'AND' | 'OR'; type MatchTypes = 'exact' | 'fuzzy' | 'like' | 'greaterThan' | 'greaterThanEqual' | 'lessThan' | 'lessThanEqual'; interface RsResponseData { data: T; } interface RsErrorData { err: string; msg: string; stack?: string; } interface RsPagedResponseData extends RsResponseData { total: number; } interface PageQuery { page: number; perPage: number; sortBy: string; sortOrder: StandardOrderTypes; filter?: string; [key: string]: string | number | boolean | object | null | undefined; } interface AuthenticatedRequesterDetails { role: string; scopes: string[]; userId?: number; [key: string]: string | number | boolean | object | null | undefined; } type OnValidAuthenticationCallback = (authenticatedRequesterDetails: AuthenticatedRequesterDetails) => void; type AuthenticateHandler = (req: RsRequest, res: RsResponse, onValid: OnValidAuthenticationCallback) => Promise; interface RsHeaders extends IncomingHttpHeaders { 'x-auth-token'?: string; } type ApiMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS'; type RequesterDetails = { role: string; scopes: string[]; host: string; ipAddress: string; userId?: number; isSystemUser?: boolean; } & T; interface RsRequest> extends express.Request { requesterDetails: RequesterDetails; data: T; routeData?: RouteData; } type DynamicObject = { [key: string]: T; }; interface RsResponse extends express.Response { sendData: (data: T, statusCode?: number) => void; sendNoWrap: (data: T, statusCode?: number) => void; sendError: (err: ErrorCode, msg: string, htmlStatusCode?: HtmlStatusCodes, stack?: string) => void; sendPaginated: (pagedData: RsPagedResponseData, statusCode?: number) => void; _contentLength?: number; } type RsRouteHandler = (req: RsRequest, res: RsResponse, next?: express.NextFunction) => Promise; interface AsyncExpressApplication { get: (url: string, handler: RsRouteHandler, nextFunction?: RsRouteHandler) => Promise | void; post: (url: string, handler: RsRouteHandler, nextFunction?: RsRouteHandler) => Promise | void; put: (url: string, handler: RsRouteHandler, nextFunction?: RsRouteHandler) => Promise | void; patch: (url: string, handler: RsRouteHandler, nextFunction?: RsRouteHandler) => Promise | void; delete: (url: string, handler: RsRouteHandler, nextFunction?: RsRouteHandler) => Promise | void; } type EventType = 'DATABASE_ROW_DELETE' | 'DATABASE_ROW_INSERT' | 'DATABASE_COLUMN_UPDATE' | 'WEBHOOK'; type MutationType = 'INSERT' | 'UPDATE' | 'DELETE'; interface SqlMutationData { mutationType: MutationType; queryMetadata: QueryMetadata; } interface DatabaseActionData { tableName: string; queryMetadata: QueryMetadata; } interface ActionRowInsertData extends DatabaseActionData { insertedId: number; insertObject: T; } interface ActionRowDeleteData extends DatabaseActionData { deletedId: number; deletedRow: T; } interface ActionColumnChangeData extends DatabaseActionData { tableName: string; changedId: number; newData: T; oldData: T; } interface ActionRowInsertFilter { tableName: string; } interface ActionRowDeleteFilter { tableName: string; } interface ActionColumnChangeFilter { tableName: string; columns: string[]; } type TriggerResult = { table: string; insertedId?: number; changedId?: number; deletedId?: number; queryMetadata: QueryMetadata; record: DynamicObject; previousRecord: DynamicObject; requesterId: number; }; type QueryMetadata = Partial & DynamicObject & { connectionInstanceId?: UUID; }; type NotifyValidationMode = 'off' | 'warn' | 'strict'; interface FireActionOptions { rethrowHandlerErrors?: boolean; } declare class EventManager { private actionHandlers; addRowInsertHandler(onInsert: (data: ActionRowInsertData, queryMetadata: QueryMetadata) => Promise, filter?: ActionRowInsertFilter): void; addColumnChangeHandler(onUpdate: (data: ActionColumnChangeData, queryMetadata: QueryMetadata) => Promise, filter: ActionColumnChangeFilter): void; addRowDeleteHandler(onDelete: (data: ActionRowDeleteData, queryMetadata: QueryMetadata) => Promise, filter?: ActionRowDeleteFilter): void; fireActionFromDbTrigger(sqlMutationData: SqlMutationData, result: TriggerResult, options?: FireActionOptions): Promise; private fireInsertActions; private fireDeleteActions; private fireUpdateActions; validateHandlersAgainstSchema(schema: ResturaSchema, mode: NotifyValidationMode): void; private hasHandlersForEventType; } declare const eventManager: EventManager; declare function apiGenerator(schema: ResturaSchema): Promise; declare function modelGenerator(schema: ResturaSchema): Promise; declare function resturaGlobalTypesGenerator(): string; declare const resturaConfigSchema: z.ZodObject<{ authToken: z.ZodString; sendErrorStackTrace: z.ZodDefault; schemaFilePath: z.ZodDefault; customApiFolderPath: z.ZodDefault; generatedTypesPath: z.ZodDefault; fileTempCachePath: z.ZodOptional; scratchDatabaseSuffix: z.ZodOptional; eventDelivery: z.ZodDefault>; eventOutbox: z.ZodPrefault; pollIntervalMs: z.ZodDefault; batchSize: z.ZodDefault; maxAttempts: z.ZodDefault; pruneAfterDays: z.ZodDefault; }, z.core.$strip>>; notifyValidation: z.ZodDefault>; queryMetadataKeys: z.ZodOptional, z.ZodArray]>>; }, z.core.$strip>; type ResturaConfigSchema = z.infer; declare abstract class PsqlConnection { private static queryMetadataKeys; static setQueryMetadataKeys(keys: string[] | 'ALL'): void; readonly instanceId: UUID; protected constructor(instanceId?: UUID); private buildQueryMetadata; protected abstract query = unknown[]>(query: string, values?: QueryConfigValues): Promise>; queryOne(query: string, options: unknown[], requesterDetails: RequesterDetails): Promise; queryOneSchema(query: string, params: unknown[], requesterDetails: RequesterDetails, zodSchema: z.ZodSchema): Promise; runQuery(query: string, options: unknown[], requesterDetails: RequesterDetails): Promise; runQuerySchema(query: string, params: unknown[], requesterDetails: RequesterDetails, zodSchema: z.ZodSchema): Promise; private logSqlStatement; } declare class PsqlPool extends PsqlConnection { poolConfig: PoolConfig; pool: Pool; constructor(poolConfig: PoolConfig); protected query = unknown[]>(query: string, values?: QueryConfigValues): Promise>; } declare class ResturaEngine { resturaConfig: ResturaConfigSchema; private multerCommonUpload; private resturaRouter; private publicEndpoints; private expressApp; private schema; private responseValidator; private authenticationHandler; private customTypeValidation; private standardTypeValidation; private psqlConnectionPool; private psqlEngine; /** * Initializes the Restura engine with the provided Express application. * * @param app - The Express application instance to initialize with Restura. * @returns A promise that resolves when the initialization is complete. */ init(app: express.Express, authenticationHandler: AuthenticateHandler, psqlConnectionPool: PsqlPool, options?: { logger?: ResturaLogger; }): Promise; /** * Determines if a given endpoint is public based on the HTTP method and full URL. This * is determined on whether the endpoint in the schema has no roles or scopes assigned to it. * * @param method - The HTTP method (e.g., 'GET', 'POST', 'PUT', 'PATCH', 'DELETE'). * @param fullUrl - The full URL of the endpoint. * @returns A boolean indicating whether the endpoint is public. */ isEndpointPublic(method: string, fullUrl: string): boolean; /** * Checks if an endpoint exists for a given HTTP method and full URL. * * @param method - The HTTP method to check (e.g., 'GET', 'POST', 'PUT', 'PATCH', 'DELETE'). * @param fullUrl - The full URL of the endpoint to check. * @returns `true` if the endpoint exists, otherwise `false`. */ doesEndpointExist(method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', fullUrl: string): boolean; /** * Generates an API from the provided schema and writes it to the specified output file. * * @param outputFile - The path to the file where the generated API will be written. * @param providedSchema - The schema from which the API will be generated. * @returns A promise that resolves when the API has been successfully generated and written to the output file. */ generateApiFromSchema(outputFile: string, providedSchema: ResturaSchema): Promise; /** * Generates a model from the provided schema and writes it to the specified output file. * * @param outputFile - The path to the file where the generated model will be written. * @param providedSchema - The schema from which the model will be generated. * @returns A promise that resolves when the model has been successfully written to the output file. */ generateModelFromSchema(outputFile: string, providedSchema: ResturaSchema): Promise; /** * Generates the ambient module declaration for Restura global types and writes it to the specified output file. * These types are used sometimes in the CustomTypes * @param outputFile */ generateResturaGlobalTypes(outputFile: string): void; /** * Retrieves the latest file system schema for Restura. * * @returns {Promise} A promise that resolves to the latest Restura schema. * @throws {Error} If the schema file is missing or the schema is not valid. */ getLatestFileSystemSchema(): Promise; private reloadEndpoints; private initializeGeneratedTypesFolder; private resturaAuthentication; private previewCreateSchema; private updateSchema; private updateTypes; private getSchema; private getSchemaAndTypes; private getMulterFilesIfAny; private attachRouteData; private executeRouteLogic; private isCustomRoute; private runCustomRouteLogic; private storeFileSystemSchema; private resetPublicEndpoints; private validateAuthorization; private getRouteData; } declare const restura: ResturaEngine; declare const filterPsqlParser: pegjs.Parser; declare abstract class SqlEngine { runQueryForRoute(req: RsRequest, routeData: StandardRouteData, schema: ResturaSchema): Promise; protected getTableSchema(schema: ResturaSchema, tableName: string): TableData; protected canRequesterAccessColumn(requesterRole: string | undefined | null, requesterScopes: string[] | undefined | null, schema: ResturaSchema, item: ResponseData, joins: JoinData[]): boolean; protected canRequesterAccessTable(requesterRole: string | undefined, requesterScopes: string[] | undefined, schema: ResturaSchema, tableName: string): boolean; protected abstract generateJoinStatements(req: RsRequest, joins: JoinData[], baseTable: string, routeData: StandardRouteData, schema: ResturaSchema, sqlParams: string[]): string; protected abstract generateGroupBy(routeData: StandardRouteData): string; protected abstract generateOrderBy(req: RsRequest, routeData: StandardRouteData): string; protected abstract generateWhereClause(req: RsRequest, where: WhereData[], routeData: StandardRouteData, sqlParams: string[]): string; protected replaceParamKeywords(value: string | number, routeData: RouteData, req: RsRequest, sqlParams: string[]): string | number; /** * Replaces local parameter keywords (e.g., $paramName) in SQL query strings with '?' placeholders * and adds the corresponding parameter values to the sqlParams array for parameterized queries. * * Validates that each parameter keyword exists in the route's request schema before processing. * If the value is not a string or routeData has no request schema, returns the value unchanged. * * @param value - The string or number value that may contain parameter keywords (e.g., "$userId") * @param routeData - The route data containing the request schema for parameter validation * @param req - The request object containing the actual parameter values in req.data * @param sqlParams - Array to which parameter values are appended (modified by reference) * @returns The value with parameter keywords replaced by '?' placeholders, or the original value if unchanged * @throws {RsError} If a parameter keyword is found that doesn't exist in the route's request schema */ protected replaceLocalParamKeywords(value: string | number, routeData: RouteData, req: RsRequest, sqlParams: string[]): string | number; protected replaceGlobalParamKeywords(value: string | number, routeData: RouteData, req: RsRequest, sqlParams: string[]): string | number; abstract generateDatabaseSchemaFromSchema(schema: ResturaSchema): string; abstract diffDatabaseToSchema(schema: ResturaSchema): Promise; protected abstract createNestedSelect(req: RsRequest, schema: ResturaSchema, item: ResponseData, routeData: StandardRouteData, sqlParams: string[]): string; protected abstract executeCreateRequest(req: RsRequest, routeData: StandardRouteData, schema: ResturaSchema): Promise; protected abstract executeGetRequest(req: RsRequest, routeData: StandardRouteData, schema: ResturaSchema): Promise; protected abstract executeUpdateRequest(req: RsRequest, routeData: StandardRouteData, schema: ResturaSchema): Promise; protected abstract executeDeleteRequest(req: RsRequest, routeData: StandardRouteData, schema: ResturaSchema): Promise; } interface EventOutboxOptions { channel: string; pollIntervalMs: number; batchSize: number; maxAttempts: number; pruneAfterDays: number; } declare const defaultEventOutboxOptions: EventOutboxOptions; interface EventDeliveryConfig { mode: 'direct' | 'outbox'; outbox?: Partial; } interface OutboxRow { id: number; createdOn: string; tableName: string; operation: MutationType; recordId: number | null; record: DynamicObject | null; previousRecord: DynamicObject | null; queryMetadata: QueryMetadata | null; processedOn: string | null; attempts: number; nextAttemptOn: string | null; isDeadLetter: boolean; } declare class EventOutboxConsumer { private psqlConnectionPool; private options; private isDraining; private drainRequested; private isStopped; private pollTimer; private pruneTimer; private activeDrain; private lastDrainOn; constructor(psqlConnectionPool: PsqlPool, options?: Partial); get channel(): string; start(): void; stop(): Promise; /** Safe to call at any frequency; overlapping calls coalesce into one extra pass. */ drain(): Promise; getStats(): Promise<{ pendingCount: number; deadLetterCount: number; lastDrainOn: string | null; }>; private processBatch; private processRow; private prune; } interface EventListenerHealth { connected: boolean; lastHeartbeatOn: string | null; reconnectAttempts: number; } declare class PsqlEngine extends SqlEngine { private psqlConnectionPool; setupTriggerListeners: Promise | undefined; private triggerClient; private scratchDbName; private reconnectAttempts; private readonly INITIAL_RECONNECT_DELAY; private readonly MAX_RECONNECT_DELAY; private readonly HEARTBEAT_INTERVAL_MS; private eventDelivery; private outboxConsumer; private heartbeatTimer; private isListenerConnected; private isReconnecting; private isClosed; private lastHeartbeatOn; constructor(psqlConnectionPool: PsqlPool, shouldListenForDbTriggers?: boolean, scratchDatabaseSuffix?: string, eventDelivery?: EventDeliveryConfig); close(): Promise; getEventListenerHealth(): EventListenerHealth; getOutboxConsumer(): EventOutboxConsumer | undefined; /** * Setup the return types for the PostgreSQL connection. * For example return DATE as a string instead of a Date object and BIGINT as a number instead of a string. */ private setupPgReturnTypes; private reconnectTriggerClient; private startHeartbeat; private listenForDbTriggers; private warnIfOutboxBacklogInDirectMode; private handleTrigger; createDatabaseFromSchema(schema: ResturaSchema, connection: PsqlPool): Promise; generateDatabaseSchemaFromSchema(schema: ResturaSchema): string; diffDatabaseToSchema(schema: ResturaSchema): Promise; private schemaGenerationOptions; protected createNestedSelect(req: RsRequest, schema: ResturaSchema, item: ResponseData, routeData: StandardRouteData, sqlParams: string[]): string; protected executeCreateRequest(req: RsRequest, routeData: StandardRouteData, schema: ResturaSchema): Promise; protected executeGetRequest(req: RsRequest, routeData: StandardRouteData, schema: ResturaSchema): Promise; /** * Executes an update request. The request will pull out the id and baseSyncVersion from the request body. * (If Present) The baseSyncVersion is used to check if the record has been modified since the last sync. * If the update fails because the baseSyncVersion has changed, a conflict error will be thrown. * IDs can not be updated using this method. * @param req - The request object. * @param routeData - The route data object. * @param schema - The schema object. * @returns The response object. */ protected executeUpdateRequest(req: RsRequest, routeData: StandardRouteData, schema: ResturaSchema): Promise; protected executeDeleteRequest(req: RsRequest, routeData: StandardRouteData, schema: ResturaSchema): Promise; protected generateJoinStatements(req: RsRequest, joins: JoinData[], baseTable: string, routeData: StandardRouteData | CustomRouteData, schema: ResturaSchema, sqlParams: string[]): string; protected generateGroupBy(routeData: StandardRouteData): string; protected generateOrderBy(req: RsRequest, routeData: StandardRouteData): string; protected generateWhereClause(req: RsRequest, where: WhereData[], routeData: StandardRouteData | CustomRouteData, sqlParams: string[]): string; } interface DbColumn { name: string; udtName: string; isNullable: boolean; columnDefault: string | null; characterMaximumLength: number | null; numericPrecision: number | null; numericScale: number | null; } interface DbIndex { name: string; tableName: string; isUnique: boolean; isPrimary: boolean; columns: string[]; using?: string; opclasses?: (string | null)[]; isValid?: boolean; order: 'ASC' | 'DESC'; where: string | null; } interface DbForeignKey { name: string; tableName: string; columns: string[]; refTable: string; refColumns: string[]; onDelete: string; onUpdate: string; } interface DbCheckConstraint { name: string; tableName: string; expression: string; } interface DbTable { name: string; columns: DbColumn[]; indexes: DbIndex[]; foreignKeys: DbForeignKey[]; checkConstraints: DbCheckConstraint[]; } interface DbSnapshot { tables: DbTable[]; extensions?: string[]; } declare function introspectDatabase(pool: PsqlPool): Promise; declare function diffSchemaToDatabase(schema: ResturaSchema, snapshot: DbSnapshot): string[]; declare const systemUser: RequesterDetails; declare function schemaToPsqlType(column: ColumnData): string; declare function buildIndexColumnSql(entry: IndexColumnData, method: string, order: string): string; declare function buildForeignKeyClause(foreignKey: ForeignKeyData): string; declare const OUTBOX_TABLE_NAME = "dbEventOutbox"; declare const DEFAULT_OUTBOX_CHANNEL = "restura_outbox"; declare function isSensitiveColumnName(columnName: string): boolean; interface TriggerSqlOptions { delivery?: 'direct' | 'outbox'; channel?: string; tableColumns?: string[]; columnTypes?: Record; } interface SchemaGenerationOptions { eventDelivery?: 'direct' | 'outbox'; outboxChannel?: string; } declare function createOutboxTableSql(): string; type NotifyConfig = ResturaSchema['database'][0]['notify']; declare function resolveNotifyColumns(tableName: string, notify: NotifyConfig, tableColumns?: string[]): string[]; declare function createInsertTriggerSql(tableName: string, notify: ResturaSchema['database'][0]['notify'], options?: TriggerSqlOptions): string; declare function createUpdateTriggerSql(tableName: string, notify: ResturaSchema['database'][0]['notify'], options?: TriggerSqlOptions): string; declare function createDeleteTriggerSql(tableName: string, notify: ResturaSchema['database'][0]['notify'], options?: TriggerSqlOptions): string; declare function generateNotifyTriggersSql(schema: ResturaSchema, options?: SchemaGenerationOptions): string[]; declare function generateDatabaseSchemaFromSchema(schema: ResturaSchema, options?: SchemaGenerationOptions): string; declare function getNewPublicSchemaAndScratchPool(targetPool: PsqlPool, scratchDbName: string): Promise; declare function diffDatabaseToSchema(schema: ResturaSchema, targetPool: PsqlPool, scratchDbName: string, options?: SchemaGenerationOptions): Promise; declare class PsqlTransaction extends PsqlConnection { clientConfig: ClientConfig; client: Client; private beginTransactionPromise; private connectPromise; constructor(clientConfig: ClientConfig, instanceId?: UUID); close(): Promise; private beginTransaction; rollback(): Promise>; commit(): Promise>; release(): Promise; protected query = unknown[]>(query: string, values?: QueryConfigValues): Promise>; } /** * This method does a couple of things: * 1. It escapes the column name to prevent SQL injection by removing any double quotes. * 2. It wraps the column name in double quotes to prevent any issues with reserved words or casing. * 3. It replaces any periods in the column name with a period wrapped in double quotes to prevent any issues with schema names. * NOTE: I looked into using pg-format ident() method but that will strip the double quotes when not needed. * @param columnName * @returns */ declare function escapeColumnName(columnName: string | undefined): string; /** * Converts a query with question marks to a query with numbered parameters, * however it ignores question marks inside single or double quotes. * @param query PostgreSQL query with question marks * @returns A string with numbered parameters such as $1, $2 in replacement of question marks */ declare function questionMarksToOrderedParams(query: string): string; /** * Creates a query to insert an object into a table. * @param table Table name to insert the object into * @param obj Data to insert into the table * @param options.customSelect Optional custom SELECT clause that wraps the INSERT in a CTE. * When provided, the INSERT is wrapped in `WITH inserted AS (INSERT ... RETURNING *)`, * and your customSelect should reference the `inserted` alias. * Example: `{ customSelect: 'SELECT i.*, u.email FROM inserted i JOIN "user" u ON i."userId" = u.id' }` * @returns The query to insert the object into the table */ declare function insertObjectQuery(table: string, obj: DynamicObject, options?: { customSelect?: string; }): string; /** * Creates a query to update an object in a table. * @param table Table name to update the object in * @param obj Data to update in the table * @param whereStatement Where clause to determine which rows to update * @param incrementSyncVersion Whether to increment the syncVersion column * @returns the query to update the object in the table */ declare function updateObjectQuery(table: string, obj: DynamicObject, whereStatement: string, incrementSyncVersion?: boolean): string; declare function isValueNumber(value: unknown): value is number; /** * This method is used to format a query and escape user input. * Use this with the SQL tag to escape user input. For example: * SQL`UPDATE "USER" SET "firstName" = ${firstName}, "isActive" = ${isActive} WHERE "id" = ${id} RETURNING *` * @param strings template strings array * @param values values to escape * @returns An escaped query with user input */ declare function SQL(strings: TemplateStringsArray, ...values: unknown[]): string; /** * Converts a JavaScript value to its PostgreSQL literal representation. * Used for debug logging of SQL queries — NOT for building actual queries * (use parameterized queries for that). * * @example * toSqlLiteral('hello') // "'hello'" * toSqlLiteral(42) // "42" * toSqlLiteral(true) // "TRUE" * toSqlLiteral(null) // "NULL" * toSqlLiteral([1, 2]) // "ARRAY[1, 2]" */ declare function toSqlLiteral(value: unknown): string; export { type ActionColumnChangeData, type ActionColumnChangeFilter, type ActionRowDeleteData, type ActionRowDeleteFilter, type ActionRowInsertData, type ActionRowInsertFilter, type ApiMethod, type AsyncExpressApplication, type AuthenticateHandler, type AuthenticatedRequesterDetails, type ConjunctionTypes, DEFAULT_OUTBOX_CHANNEL, type DatabaseActionData, type DbCheckConstraint, type DbColumn, type DbForeignKey, type DbIndex, type DbSnapshot, type DbTable, type DynamicObject, type ErrorCode, type EventDeliveryConfig, type EventListenerHealth, EventManager, EventOutboxConsumer, type EventOutboxOptions, type EventType, type FireActionOptions, HtmlStatusCodes, type LogLevel, type MatchTypes, type MutationType, type NotifyValidationMode, OUTBOX_TABLE_NAME, type OnValidAuthenticationCallback, type OutboxRow, type PageQuery, PsqlConnection, PsqlEngine, PsqlPool, PsqlTransaction, type QueryMetadata, type RequesterDetails, type ResturaConfigSchema, type ResturaLogger, type ResturaSchema, RsError, type RsErrorData, type RsErrorInternalData, type RsHeaders, type RsPagedResponseData, type RsRequest, type RsResponse, type RsResponseData, type RsRouteHandler, SQL, type SchemaChangeValue, type SchemaGenerationOptions, type SchemaPreview, type SqlMutationData, type StandardOrderTypes, type TriggerResult, type TriggerSqlOptions, apiGenerator, buildForeignKeyClause, buildIndexColumnSql, createDeleteTriggerSql, createInsertTriggerSql, createOutboxTableSql, createUpdateTriggerSql, defaultEventOutboxOptions, diffDatabaseToSchema, diffSchemaToDatabase, escapeColumnName, eventManager, filterPsqlParser, generateDatabaseSchemaFromSchema, generateNotifyTriggersSql, getNewPublicSchemaAndScratchPool, insertObjectQuery, introspectDatabase, isSchemaValid, isSensitiveColumnName, isValueNumber, logger, modelGenerator, questionMarksToOrderedParams, resolveNotifyColumns, restura, resturaGlobalTypesGenerator, resturaSchema, schemaToPsqlType, setLogger, systemUser, toSqlLiteral, updateObjectQuery };