import type { IncomingMessage, ServerResponse } from 'node:http'; import type { Store } from './db/store.ts'; import type { NackErrorInput } from './protocol/errors.ts'; export type Row = Record; export interface GenesisMessage { MESSAGE_TYPE: string; SOURCE_REF?: string; DETAILS?: Row; [key: string]: any; } export type GenesisFieldType = 'STRING' | 'ENUM' | 'INT' | 'SHORT' | 'LONG' | 'DOUBLE' | 'BIGDECIMAL' | 'BOOLEAN' | 'DATE' | 'DATETIME' | 'NANO_TIMESTAMP' | 'RAW'; export interface FieldDef { name: string; type: GenesisFieldType | NestedFieldType; nullable?: boolean; values?: string[]; default?: unknown; maxSize?: number; scale?: number; minLength?: number; min?: number; max?: number; description?: string; title?: string; optional?: boolean; items?: FieldTypeDef; unique?: boolean; className?: string; fields?: FieldDef[]; enumClass?: string; dao?: boolean; } export type NestedFieldType = 'ARRAY' | 'MAP' | 'OBJECT' | 'ANY'; export type FieldTypeDef = Omit; export interface GeneratedFieldDef { field: string; kind: 'SEQUENCE' | 'AUTO_INCREMENT'; prefix?: string; } export type CrudEventVerb = 'INSERT' | 'MODIFY' | 'DELETE'; export interface IndexDef { name: string; fields: string[]; unique?: boolean; } export interface TableDef { pkField: string | string[]; sequencePrefix?: string; sequenceWidth?: number; sequenceFormat?: SequenceFormat; rows?: Row[]; fields?: FieldDef[]; generated?: GeneratedFieldDef[]; indexes?: IndexDef[]; events?: CrudEventVerb[]; audit?: boolean | AuditDef; } export interface AuditDef { sequencePrefix?: string; } export type SequenceFormat = 'prefix' | 'genesis'; export type JoinFieldLink = [string, string]; export interface JoinAliasedFieldLink { fromAlias?: string; from: string; to: string; } export interface JoinValueLink { to: string; value: unknown; } export type JoinLink = JoinFieldLink | JoinAliasedFieldLink | JoinValueLink; export type JoinType = 'INNER' | 'OUTER'; export interface ViewRowSources { tables: Record; } export interface JoinDef { table: string; alias?: string; fromAlias?: string; on: JoinFieldLink | JoinLink[]; type?: JoinType; backwardsJoin?: boolean; select?: Record; where?: (joinRow: Row, viewRow: Row, sources: ViewRowSources) => boolean; } export interface AntiJoinDef { table: string; on: JoinFieldLink | JoinLink[]; where?: (joinRow: Row, baseRow: Row, sources: ViewRowSources) => boolean; } export interface ViewDef { base: string; baseAlias?: string; pkField?: string | string[]; joins?: JoinDef[]; antiJoins?: AntiJoinDef[]; select?: Record; derived?: Record any>; derivedTypes?: Record>; fields?: string[]; } export interface RequestingUser { userName?: string; profile: string[]; permission: string[]; } export interface ResourceAuthDef { rowFilter?: (row: Row, user: RequestingUser) => boolean; hideFields?: string[] | ((user: RequestingUser) => string[]); } export interface QueryDef { source: string; fieldTypes?: Record; fields?: string[]; indexes?: IndexDef[]; legacyEngine?: boolean; filter?: (row: Row) => boolean; auth?: ResourceAuthDef; } export interface RequestReplyResolverCtx { request: Row; message: GenesisMessage; user: RequestingUser; store: Store; config: MockServerConfig; } export interface RequestReplyDef { source?: string; resolver?: (ctx: RequestReplyResolverCtx) => Row[]; metadataRows?: Row[]; fieldTypes?: Record; filter?: (row: Row) => boolean; auth?: ResourceAuthDef; criteriaOnly?: boolean; requestFields?: string[]; fields?: string[]; rowLimit?: number; order?: (a: Row, b: Row) => number; typed?: TypedRequestReply; } export interface TypedRequestReply { request: { className: string; fields: FieldDef[]; }; reply: { className: string; fields: FieldDef[]; }; meta?: Row; } export interface EventSchemaOverride { table?: string; pkOnly?: boolean; fieldTypes?: Record; fields?: FieldDef[]; className?: string; } export interface ResourceItem { RESOURCE_NAME: string; RESOURCE_TYPE: 'DATASERVER' | 'REQUEST_SERVER' | 'EVENT_HANDLER'; } export interface AuthUser { username: string; password?: string; profile?: string[]; permission?: string[]; user?: Row; loginAck?: Row; status?: string; } export interface AuthDirectory { user(username: string, store: Store): AuthUser | undefined; login(username: string, password: string | undefined, ctx: LoginCtx): AuthUser | LoginFailure; refresh(username: string | undefined, ctx: LoginCtx): AuthUser | LoginFailure; logout(username: string, online: boolean, ctx: LoginCtx): void; } export type LoginFailure = 'INCORRECT_CREDENTIALS' | 'PASSWORD_EXPIRED' | 'LOCKED_ACCOUNT' | 'MAX_TRIES_REACHED' | 'UNKNOWN_ACCOUNT' | 'NO_COMPATIBLE_PROVIDERS'; export interface LoginCtx { store: Store; broadcastTableChange: BroadcastTableChangeFn; hostname: string; messageUser: string | null; } export interface CookieAuthenticationOptions { httpOnly?: boolean; secure?: boolean; sameSite?: 'Strict' | 'Lax' | 'None'; path?: string; domain?: string; sessionCookies?: boolean; } export interface ProductDetails { NAME: string; VERSION: string; } export interface SsoIdentityProvider { ID: string; DESCRIPTION?: string; TYPE?: string; [key: string]: any; } export interface AuthConfig { users?: AuthUser[]; directory?: AuthDirectory; ssoIdentityProviders?: SsoIdentityProvider[]; heartbeatIntervalSecs?: number; sessionTimeoutMins?: number; refreshTokenExpirationMins?: number; products?: ProductDetails[]; cookieAuthentication?: boolean | CookieAuthenticationOptions; httpAutoLogin?: string | true; requireAuth?: boolean; } export interface BroadcastFn { (resourceName: string, operation: 'INSERT' | 'MODIFY' | 'DELETE', row: Row): void; } export interface BroadcastTableChangeFn { (tableName: string, operation: 'INSERT' | 'MODIFY' | 'DELETE', pk: string): void; } export interface EventHandlerCtx { connection: import('./protocol/connection.ts').Connection; store: Store; message: GenesisMessage; config: MockServerConfig; broadcast: BroadcastFn; broadcastTableChange: BroadcastTableChangeFn; validate: boolean; ignoreWarnings: boolean; afterCommit(callback: () => void): void; } export type EventHandlerResult = { generated?: Row[]; warnings?: NackErrorInput[]; reply?: Row; } | void; export type EventHandler = (details: Row, ctx: EventHandlerCtx) => EventHandlerResult; export interface HttpRouteDef { method?: string; path: string | RegExp; handler: (req: IncomingMessage, res: ServerResponse, ctx: HttpRouteCtx) => void | Promise; } export interface HttpRouteCtx { store: Store; broadcast: BroadcastFn; broadcastTableChange: BroadcastTableChangeFn; userName?: string; commitEvent(message: GenesisMessage, userName: string | undefined): Row; } export interface OnMessageCtx { connection: import('./protocol/connection.ts').Connection; store: Store; broadcast: BroadcastFn; broadcastTableChange: BroadcastTableChangeFn; } export type OnMessageHook = (message: GenesisMessage, ctx: OnMessageCtx) => GenesisMessage | false | void; export type FidelityMode = 'gsf' | 'legacy'; export type LiveModifyMode = 'diff' | 'full'; export interface MockServerConfig { fidelity?: FidelityMode; liveModify?: LiveModifyMode; tables?: Record; views?: Record; queries?: Record; requestReplies?: Record; eventHandlers?: Record; defaultEventHandler?: EventHandler; eventSchemas?: Record; auth?: AuthConfig; resources?: ResourceItem[]; additionalResources?: ResourceItem[]; httpRoutes?: HttpRouteDef[]; onMessage?: OnMessageHook; productName?: string; appName?: string; } export interface FieldMetadata { NAME: string; TYPE: string; VALID_VALUES?: string[]; } export interface SocketLike { readyState: number; send(data: string): void; }