import { RamConfig, RamContext, RamFlags, RamStorage, RawRamQuery } from "./types"; import { RamStatement } from "./RamStatement"; import { Repository } from "../repository/Repository"; import { Dispatch } from "../persistence/Dispatch"; import { Adapter, AdapterFlags, RawResult } from "../persistence"; import { Model } from "@decaf-ts/decorator-validation"; import { BaseError, OperationKeys, PrimaryKeyType } from "@decaf-ts/db-decorators"; import type { Constructor } from "@decaf-ts/decoration"; import { RamPaginator } from "./RamPaginator"; import { ContextualArgs } from "../utils/ContextualLoggedClass"; /** * @description In-memory adapter for data persistence * @summary The RamAdapter provides an in-memory implementation of the persistence layer. * It stores data in JavaScript Maps and provides CRUD operations and query capabilities. * This adapter is useful for testing, prototyping, and applications that don't require * persistent storage across application restarts. * @class RamAdapter * @category Ram * @example * ```typescript * // Create a new RAM adapter * const adapter = new RamAdapter('myRamAdapter'); * * // Create a repository for a model * const userRepo = new (adapter.repository())(User, adapter); * * // Perform CRUD operations * const user = new User({ name: 'John', email: 'john@example.com' }); * await userRepo.create(user); * const retrievedUser = await userRepo.findById(user.id); * ``` * @mermaid * sequenceDiagram * participant Client * participant Repository * participant RamAdapter * participant Storage as In-Memory Storage * * Client->>Repository: create(model) * Repository->>RamAdapter: create(tableName, id, model) * RamAdapter->>RamAdapter: lock.acquire() * RamAdapter->>Storage: set(id, model) * RamAdapter->>RamAdapter: lock.release() * RamAdapter-->>Repository: model * Repository-->>Client: model * * Client->>Repository: findById(id) * Repository->>RamAdapter: read(tableName, id) * RamAdapter->>Storage: get(id) * Storage-->>RamAdapter: model * RamAdapter-->>Repository: model * Repository-->>Client: model */ export declare class RamAdapter extends Adapter { constructor(conf?: RamConfig, alias?: string); /** * @description Gets the repository constructor for a model * @summary Returns a constructor for creating repositories that work with the specified model type. * This method overrides the base implementation to provide RAM-specific repository functionality. * @template M - The model type for the repository * @return {Constructor>} A constructor for creating RAM repositories */ repository>>(): Constructor; /** * @description Creates operation flags with UUID * @summary Extends the base flags with a UUID for user identification. * This method ensures that all operations have a unique identifier for tracking purposes. * @template M - The model type for the operation * @param {OperationKeys} operation - The type of operation being performed * @param {Constructor} model - The model constructor * @param {Partial} flags - Partial flags to be extended * @return {Promise} Complete flags with UUID */ flags>(operation: OperationKeys, model: Constructor, flags: Partial): Promise; protected Dispatch(): Dispatch; private indexes; private lock; /** * @description Indexes models in the RAM adapter * @summary A no-op indexing method for the RAM adapter. * Since RAM adapter doesn't require explicit indexing, this method simply resolves immediately. * @param models - Models to be indexed (unused) * @return {Promise} A promise that resolves when indexing is complete */ index(...models: Record[]): Promise; /** * @description Prepares a model for storage * @summary Converts a model instance to a format suitable for storage in the RAM adapter. * This method extracts the primary key and creates a record without the primary key field. * @template M - The model type being prepared * @param {M} model - The model instance to prepare * @param pk - The primary key property name * @return Object containing the record and ID */ prepare(model: M, ...args: [...any[], RamContext]): { record: Record; id: string; transient?: Record; }; /** * @description Converts a stored record back to a model instance * @summary Reconstructs a model instance from a stored record by adding back the primary key. * This method is the inverse of the prepare method. * @template M - The model type to revert to * @param {Record} obj - The stored record * @param {Constructor} clazz - The model class or name * @param {PrimaryKeyType} id - The primary key value * @return {M} The reconstructed model instance */ revert(obj: Record, clazz: Constructor, id: PrimaryKeyType, transient?: Record, ...args: [...any[], RamContext]): M; /** * @description Creates a new record in the in-memory storage * @summary Stores a new record in the specified table with the given ID. * This method acquires a lock to ensure thread safety, creates the table if it doesn't exist, * checks for conflicts, and stores the model. * @param {string} clazz - The name of the table to store the record in * @param {string | number} id - The unique identifier for the record * @param {Record} model - The record data to store * @return {Promise>} A promise that resolves to the stored record * @mermaid * sequenceDiagram * participant Caller * participant RamAdapter * participant Storage as In-Memory Storage * * Caller->>RamAdapter: create(tableName, id, model) * RamAdapter->>RamAdapter: lock.acquire() * RamAdapter->>Storage: has(tableName) * alt Table doesn't exist * RamAdapter->>Storage: set(tableName, new Map()) * end * RamAdapter->>Storage: has(id) * alt Record exists * RamAdapter-->>Caller: throw ConflictError * end * RamAdapter->>Storage: set(id, model) * RamAdapter->>RamAdapter: lock.release() * RamAdapter-->>Caller: model */ create(clazz: Constructor, id: PrimaryKeyType, model: Record, ...args: ContextualArgs): Promise>; /** * @description Retrieves a record from in-memory storage * @summary Fetches a record with the specified ID from the given table. * This method checks if the table and record exist and throws appropriate errors if not. * @param {Constructor} clazz - The name of the table to retrieve from * @param {PrimaryKeyType} id - The unique identifier of the record to retrieve * @return {Promise>} A promise that resolves to the retrieved record * @mermaid * sequenceDiagram * participant Caller * participant RamAdapter * participant Storage as In-Memory Storage * * Caller->>RamAdapter: read(tableName, id) * RamAdapter->>Storage: has(tableName) * alt Table doesn't exist * RamAdapter-->>Caller: throw NotFoundError * end * RamAdapter->>Storage: has(id) * alt Record doesn't exist * RamAdapter-->>Caller: throw NotFoundError * end * RamAdapter->>Storage: get(id) * Storage-->>RamAdapter: record * RamAdapter-->>Caller: record */ read(clazz: Constructor, id: PrimaryKeyType, ...args: ContextualArgs): Promise>; /** * @description Updates an existing record in the in-memory storage * @summary Updates a record with the specified ID in the given table. * This method acquires a lock to ensure thread safety, checks if the table and record exist, * and updates the record with the new data. * @param {string} tableName - The name of the table containing the record * @param {string | number} id - The unique identifier of the record to update * @param {Record} model - The new record data * @return {Promise>} A promise that resolves to the updated record * @mermaid * sequenceDiagram * participant Caller * participant RamAdapter * participant Storage as In-Memory Storage * * Caller->>RamAdapter: update(tableName, id, model) * RamAdapter->>RamAdapter: lock.acquire() * RamAdapter->>Storage: has(tableName) * alt Table doesn't exist * RamAdapter-->>Caller: throw NotFoundError * end * RamAdapter->>Storage: has(id) * alt Record doesn't exist * RamAdapter-->>Caller: throw NotFoundError * end * RamAdapter->>Storage: set(id, model) * RamAdapter->>RamAdapter: lock.release() * RamAdapter-->>Caller: model */ update(clazz: Constructor, id: PrimaryKeyType, model: Record, ...args: ContextualArgs): Promise>; /** * @description Deletes a record from the in-memory storage * @summary Removes a record with the specified ID from the given table. * This method acquires a lock to ensure thread safety, checks if the table and record exist, * retrieves the record before deletion, and then removes it from storage. * @param {string} tableName - The name of the table containing the record * @param {string | number} id - The unique identifier of the record to delete * @return {Promise>} A promise that resolves to the deleted record * @mermaid * sequenceDiagram * participant Caller * participant RamAdapter * participant Storage as In-Memory Storage * * Caller->>RamAdapter: delete(tableName, id) * RamAdapter->>RamAdapter: lock.acquire() * RamAdapter->>Storage: has(tableName) * alt Table doesn't exist * RamAdapter-->>Caller: throw NotFoundError * end * RamAdapter->>Storage: has(id) * alt Record doesn't exist * RamAdapter-->>Caller: throw NotFoundError * end * RamAdapter->>Storage: get(id) * Storage-->>RamAdapter: record * RamAdapter->>Storage: delete(id) * RamAdapter->>RamAdapter: lock.release() * RamAdapter-->>Caller: record */ delete(clazz: Constructor, id: PrimaryKeyType, ...args: ContextualArgs): Promise>; /** * @description Gets or creates a table in the in-memory storage * @summary Retrieves the Map representing a table for a given model or table name. * If the table doesn't exist, it creates a new one. This is a helper method used * by other methods to access the correct storage location. * @template M - The model type for the table * @param {string | Constructor} from - The model class or table name * @return {Map | undefined} The table Map or undefined */ protected tableFor(from: string | Constructor): Map | undefined; /** * @description Executes a raw query against the in-memory storage * @summary Performs a query operation on the in-memory data store using the provided query specification. * This method supports filtering, sorting, pagination, and field selection. * @template R - The return type of the query * @param {RawRamQuery} rawInput - The query specification * @return {Promise} A promise that resolves to the query results * @mermaid * sequenceDiagram * participant Caller * participant RamAdapter * participant Storage as In-Memory Storage * * Caller->>RamAdapter: raw(rawInput) * RamAdapter->>RamAdapter: tableFor(from) * alt Table doesn't exist * RamAdapter-->>Caller: throw InternalError * end * RamAdapter->>RamAdapter: findPrimaryKey(new from()) * RamAdapter->>Storage: entries() * Storage-->>RamAdapter: entries * loop For each entry * RamAdapter->>RamAdapter: revert(r, from, id, pk) * end * alt Where condition exists * RamAdapter->>RamAdapter: result.filter(where) * end * alt Sort condition exists * RamAdapter->>RamAdapter: result.sort(sort) * end * alt Skip specified * RamAdapter->>RamAdapter: result.slice(skip) * end * alt Limit specified * RamAdapter->>RamAdapter: result.slice(0, limit) * end * alt Select fields specified * loop For each result * RamAdapter->>RamAdapter: Filter to selected fields * end * end * RamAdapter-->>Caller: result */ raw(rawInput: RawRamQuery, docsOnly?: D, ...args: ContextualArgs): Promise>; /** * @description Parses and converts errors to appropriate types * @summary Ensures that errors are of the correct type for consistent error handling. * If the error is already a BaseError, it's returned as is; otherwise, it's wrapped in an InternalError. * @template V - The expected error type, extending BaseError * @param {Error} err - The error to parse * @return {V} The parsed error of the expected type */ parseError(err: Error): V; /** * @description Creates a new statement builder for queries * @summary Factory method that creates a new RamStatement instance for building queries. * This method allows for fluent query construction against the RAM adapter. * @template M - The model type for the statement * @return {RamStatement} A new statement builder instance */ Statement>(overrides?: Partial): RamStatement, RamContext>>; Paginator>(query: RawRamQuery, size: number, clazz: Constructor): RamPaginator; for(config: Partial, ...args: any[]): typeof this; /** * @description Sets up RAM-specific decorations for model properties * @summary Configures decorations for createdBy and updatedBy fields in the RAM adapter. * This static method is called during initialization to set up handlers that automatically * populate these fields with the current user's UUID during create and update operations. * @return {void} * @mermaid * sequenceDiagram * participant RamAdapter * participant Decoration * participant Repository * * RamAdapter->>Repository: key(PersistenceKeys.CREATED_BY) * Repository-->>RamAdapter: createdByKey * RamAdapter->>Repository: key(PersistenceKeys.UPDATED_BY) * Repository-->>RamAdapter: updatedByKey * * RamAdapter->>Decoration: flavouredAs(RamFlavour) * Decoration-->>RamAdapter: DecoratorBuilder * RamAdapter->>Decoration: for(createdByKey) * RamAdapter->>Decoration: define(onCreate, propMetadata) * RamAdapter->>Decoration: apply() * * RamAdapter->>Decoration: flavouredAs(RamFlavour) * Decoration-->>RamAdapter: DecoratorBuilder * RamAdapter->>Decoration: for(updatedByKey) * RamAdapter->>Decoration: define(onCreate, propMetadata) * RamAdapter->>Decoration: apply() */ static decoration(): void; protected getClient(): RamStorage; }