import "reflect-metadata";
import {
CouchDBAdapter,
CouchDBKeys,
CreateIndexRequest,
generateIndexes,
IndexError,
MangoQuery,
} from "@decaf-ts/for-couchdb";
import {
BaseError,
ConflictError,
Context,
InternalError,
NotFoundError,
onCreate,
onCreateUpdate,
OperationKeys,
} from "@decaf-ts/db-decorators";
import {
ConnectionError,
PersistenceKeys,
RelationsMetadata,
Repository,
UnsupportedError,
} from "@decaf-ts/core";
import Database = PouchDB.Database;
import Response = PouchDB.Core.Response;
import Err = PouchDB.Core.Error;
import IdMeta = PouchDB.Core.IdMeta;
import GetMeta = PouchDB.Core.GetMeta;
import CreateIndexResponse = PouchDB.Find.CreateIndexResponse;
import {
Constructor,
Decoration,
Model,
propMetadata,
} from "@decaf-ts/decorator-validation";
import BulkGetResponse = PouchDB.Core.BulkGetResponse;
import FindResponse = PouchDB.Find.FindResponse;
import { PouchConfig, PouchFlags } from "./types";
import { DefaultLocalStoragePath, PouchFlavour } from "./constants";
import { PouchRepository } from "./PouchRepository";
import PouchDB from "pouchdb-core";
import * as PouchMapReduce from "pouchdb-mapreduce";
import * as PouchReplication from "pouchdb-replication";
import * as PouchFind from "pouchdb-find";
/**
* @description Sets the creator ID on a model during creation or update operations
* @summary This function is used as a decorator handler to automatically set the creator ID field on a model
* when it's being created or updated. It extracts the UUID from the context and assigns it to the specified key.
* @template M - The model type that extends Model
* @template R - The repository type that extends PouchRepository<M>
* @template V - The relations metadata type that extends RelationsMetadata
* @param {R} this - The repository instance
* @param {Context<PouchFlags>} context - The operation context containing flags
* @param {V} data - The relations metadata
* @param key - The property key to set on the model
* @param {M} model - The model instance to modify
* @return {Promise<void>} A promise that resolves when the operation is complete
* @function createdByOnPouchCreateUpdate
* @memberOf module:for-pouch
*/
export async function createdByOnPouchCreateUpdate<
M extends Model,
R extends PouchRepository<M>,
V extends RelationsMetadata,
>(
this: R,
context: Context<PouchFlags>,
data: V,
key: keyof M,
model: M
): Promise<void> {
try {
const uuid: string = context.get("UUID");
model[key] = uuid as M[keyof M];
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e: unknown) {
throw new UnsupportedError(
"No User found in context. Please provide a user in the context"
);
}
}
/**
* @description PouchDB implementation of the CouchDBAdapter
* @summary Concrete adapter that bridges the generic CouchDBAdapter to a PouchDB backend. It supports CRUD (single and bulk), indexing and Mango queries, and wires flavour-specific decorations.
* @template PouchFlags - The flags specific to PouchDB operations
* @template Context<PouchFlags> - The context type with PouchDB flags
* @param {PouchConfig} config - Adapter configuration (remote credentials or local storage path, db name, plugins)
* @param {string} [alias] - Optional alias for the database
* @class PouchAdapter
* @example
* ```typescript
* import { PouchAdapter } from '@decaf-ts/for-pouch';
*
* // Create a PouchAdapter with config
* const adapter = new PouchAdapter({
* protocol: 'http',
* host: 'localhost:5984',
* user: 'admin',
* password: 'secret',
* dbName: 'my-database',
* plugins: []
* });
*
* // Or use local storage
* const localAdapter = new PouchAdapter({
* protocol: 'http', // ignored for local
* dbName: 'local-db',
* storagePath: 'local_dbs',
* plugins: []
* });
*
* // Use the adapter for database operations
* const result = await adapter.read('users', 'user-123');
* ```
* @mermaid
* sequenceDiagram
* participant Client
* participant PouchAdapter
* participant PouchDB
* participant CouchDB
*
* Client->>PouchAdapter: new PouchAdapter(config, alias?)
* PouchAdapter->>CouchDBAdapter: super(config, PouchFlavour, alias)
*
* Client->>PouchAdapter: create(table, id, model)
* PouchAdapter->>PouchDB: put(model)
* PouchDB->>CouchDB: HTTP PUT
* CouchDB-->>PouchDB: Response
* PouchDB-->>PouchAdapter: Response
* PouchAdapter-->>Client: Updated model
*
* Client->>PouchAdapter: read(table, id)
* PouchAdapter->>PouchDB: get(id)
* PouchDB->>CouchDB: HTTP GET
* CouchDB-->>PouchDB: Document
* PouchDB-->>PouchAdapter: Document
* PouchAdapter-->>Client: Model
*/
export class PouchAdapter extends CouchDBAdapter<
PouchConfig,
Database,
PouchFlags,
Context<PouchFlags>
> {
constructor(config: PouchConfig, alias?: string) {
super(config, PouchFlavour, alias);
}
/**
* @description Lazily initializes and returns the underlying PouchDB client
* @summary Loads required PouchDB plugins once, builds the connection URL or local storage path from config, and caches the Database instance for reuse. Throws InternalError if client creation fails.
* @return {Database} A PouchDB Database instance ready to perform operations
* @mermaid
* sequenceDiagram
* participant Caller
* participant PouchAdapter
* participant PouchDB
* Caller->>PouchAdapter: getClient()
* alt client not initialized
* PouchAdapter->>PouchAdapter: register plugins
* PouchAdapter->>PouchDB: new PouchDB(url or path)
* alt creation fails
* PouchDB-->>PouchAdapter: Error
* PouchAdapter-->>Caller: throws InternalError
* else success
* PouchDB-->>PouchAdapter: Database
* PouchAdapter-->>Caller: cached client
* end
* else client initialized
* PouchAdapter-->>Caller: cached client
* end
*/
override getClient(): Database {
if (!this._client) {
const plugins = [
PouchMapReduce,
PouchReplication,
PouchFind,
...this.config.plugins,
];
for (const plugin of plugins) {
try {
PouchDB.plugin(plugin);
} catch (e: any) {
if (e instanceof Error && e.message.includes("redefine property"))
continue; //plugin has already been loaded so it's ok
throw e;
}
}
const { host, protocol, user, password, dbName, storagePath } =
this.config;
try {
if (host && user) {
this._client = new PouchDB(
`${protocol}://${user}:${password}@${host}/${dbName}`
);
} else
this._client = new PouchDB(
`${storagePath || DefaultLocalStoragePath}/${dbName}`
);
} catch (e: unknown) {
throw new InternalError(`Failed to create PouchDB client: ${e}`);
}
}
return this._client as Database;
}
/**
* @description Generates operation flags for PouchDB operations
* @summary Creates a set of flags for a specific operation, including a UUID for identification.
* This method extracts the user ID from the database URL or generates a random UUID if not available.
* @template M - The model type that extends Model
* @param {OperationKeys} operation - The operation key (create, read, update, delete)
* @param {Constructor<M>} model - The model constructor
* @param {Partial<PouchFlags>} flags - Partial flags to be merged
* @return {Promise<PouchFlags>} The complete set of flags for the operation
*/
protected override async flags<M extends Model>(
operation: OperationKeys,
model: Constructor<M>,
flags: Partial<PouchFlags>
): Promise<PouchFlags> {
if (!this.config.user) this.config.user = crypto.randomUUID();
return Object.assign(await super.flags(operation, model, flags), {
UUID: this.config.user,
}) as PouchFlags;
}
/**
* @description Creates database indexes for the given models
* @summary Generates and creates indexes in the PouchDB database based on the provided model constructors.
* This method uses the generateIndexes utility to create index definitions and then creates them in the database.
* @template M - The model type that extends Model
* @param models - The model constructors to create indexes for
* @return {Promise<void>} A promise that resolves when all indexes are created
*/
protected async index<M extends Model>(
...models: Constructor<M>[]
): Promise<void> {
const indexes: CreateIndexRequest[] = generateIndexes(models);
for (const index of indexes) {
const res: CreateIndexResponse<any> = await this.client.createIndex(
index as any
);
const { result } = res;
if (result === "existing")
throw new ConflictError(`Index ${index.name} already exists`);
}
}
/**
* @description Creates a new document in the database
* @summary Inserts a new document into the PouchDB database using the put operation.
* This method handles error parsing and ensures the operation was successful.
* @param {string} tableName - The name of the table/collection
* @param {string|number} id - The document ID
* @param {Record<string, any>} model - The document data to insert
* @return {Promise<Record<string, any>>} A promise that resolves to the created document with metadata
* @mermaid
* sequenceDiagram
* participant Client
* participant PouchAdapter
* participant PouchDB
*
* Client->>PouchAdapter: create(tableName, id, model)
* PouchAdapter->>PouchDB: put(model)
* alt Success
* PouchDB-->>PouchAdapter: Response with ok=true
* PouchAdapter->>PouchAdapter: assignMetadata(model, response.rev)
* PouchAdapter-->>Client: Updated model with metadata
* else Error
* PouchDB-->>PouchAdapter: Error
* PouchAdapter->>PouchAdapter: parseError(e)
* PouchAdapter-->>Client: Throws error
* end
*/
async create(
tableName: string,
id: string | number,
model: Record<string, any>
): Promise<Record<string, any>> {
let response: Response;
try {
response = await this.client.put(model);
} catch (e: unknown) {
throw this.parseError(e as Error);
}
if (!response.ok)
throw new InternalError(
`Failed to insert doc id: ${id} in table ${tableName}`
);
return this.assignMetadata(model, response.rev);
}
/**
* @description Creates multiple documents in the database in a single operation
* @summary Inserts multiple documents into the PouchDB database using the bulkDocs operation.
* This method handles error parsing and ensures all operations were successful.
* @param {string} tableName - The name of the table/collection
* @param {string[]|number[]} ids - The document IDs
* @param models - The document data to insert
* @return A promise that resolves to the created documents with metadata
* @mermaid
* sequenceDiagram
* participant Client
* participant PouchAdapter
* participant PouchDB
*
* Client->>PouchAdapter: createAll(tableName, ids, models)
* PouchAdapter->>PouchDB: bulkDocs(models)
* alt Success
* PouchDB-->>PouchAdapter: Array of responses with ok=true
* PouchAdapter->>PouchAdapter: assignMultipleMetadata(models, revs)
* PouchAdapter-->>Client: Updated models with metadata
* else Error
* PouchDB-->>PouchAdapter: Array with errors
* PouchAdapter->>PouchAdapter: Check for errors
* PouchAdapter-->>Client: Throws InternalError
* end
*/
override async createAll(
tableName: string,
ids: string[] | number[],
models: Record<string, any>[]
): Promise<Record<string, any>[]> {
let response: Response[] | Err[];
try {
response = await this.client.bulkDocs(models);
} catch (e: any) {
throw PouchAdapter.parseError(e);
}
if (!response.every((r: Response | Err) => (r as Response).ok)) {
const errors = response.reduce((accum: string[], el, i) => {
if (el.error)
accum.push(
`el ${i}: ${el.error}${el.reason ? ` - ${el.reason}` : ""}`
);
return accum;
}, []);
throw new InternalError(errors.join("\n"));
}
return this.assignMultipleMetadata(
models,
response.map((r) => r.rev as string)
);
}
/**
* @description Retrieves a document from the database by ID
* @summary Fetches a document from the PouchDB database using the get operation.
* This method generates the document ID based on the table name and ID, then retrieves the document.
* @param {string} tableName - The name of the table/collection
* @param {string|number} id - The document ID
* @return {Promise<Record<string, any>>} A promise that resolves to the retrieved document with metadata
* @mermaid
* sequenceDiagram
* participant Client
* participant PouchAdapter
* participant PouchDB
*
* Client->>PouchAdapter: read(tableName, id)
* PouchAdapter->>PouchAdapter: generateId(tableName, id)
* PouchAdapter->>PouchDB: get(_id)
* alt Success
* PouchDB-->>PouchAdapter: Document
* PouchAdapter->>PouchAdapter: assignMetadata(record, record._rev)
* PouchAdapter-->>Client: Document with metadata
* else Error
* PouchDB-->>PouchAdapter: Error
* PouchAdapter->>PouchAdapter: parseError(e)
* PouchAdapter-->>Client: Throws error
* end
*/
async read(
tableName: string,
id: string | number
): Promise<Record<string, any>> {
const _id = this.generateId(tableName, id);
let record: IdMeta & GetMeta;
try {
record = await this.client.get(_id);
} catch (e: any) {
throw PouchAdapter.parseError(e);
}
return this.assignMetadata(record, record._rev);
}
/**
* @description Retrieves multiple documents from the database by their IDs
* @summary Fetches multiple documents from the PouchDB database using the bulkGet operation.
* This method generates document IDs based on the table name and IDs, then retrieves the documents.
* @param {string} tableName - The name of the table/collection
* @param {Array<string|number|bigint>} ids - The document IDs
* @return A promise that resolves to the retrieved documents with metadata
* @mermaid
* sequenceDiagram
* participant Client
* participant PouchAdapter
* participant PouchDB
*
* Client->>PouchAdapter: readAll(tableName, ids)
* PouchAdapter->>PouchAdapter: Map ids to generateId(tableName, id)
* PouchAdapter->>PouchDB: bulkGet({docs})
* alt Success
* PouchDB-->>PouchAdapter: BulkGetResponse
* PouchAdapter->>PouchAdapter: Process results
* PouchAdapter->>PouchAdapter: assignMetadata for each doc
* PouchAdapter-->>Client: Documents with metadata
* else Error
* PouchAdapter->>PouchAdapter: parseError(error)
* PouchAdapter-->>Client: Throws error
* end
*/
override async readAll(
tableName: string,
ids: (string | number | bigint)[]
): Promise<Record<string, any>[]> {
const results: BulkGetResponse<any> = await this.client.bulkGet({
docs: ids.map((id) => ({ id: this.generateId(tableName, id as any) })),
});
const res = results.results.reduce((accum: any[], r) => {
r.docs.forEach((d) => {
if ((d as any).error || !(d as any).ok)
throw PouchAdapter.parseError(
((d as { error: Err }).error as Error) ||
new InternalError("Missing valid response")
);
const result = Object.assign({}, (d as { ok: any }).ok);
accum.push(this.assignMetadata(result, (d as any).ok[CouchDBKeys.REV]));
});
return accum;
}, []);
return res;
}
/**
* @description Updates an existing document in the database
* @summary Updates a document in the PouchDB database using the put operation.
* This method handles error parsing and ensures the operation was successful.
* @param {string} tableName - The name of the table/collection
* @param {string|number} id - The document ID
* @param {Record<string, any>} model - The updated document data
* @return {Promise<Record<string, any>>} A promise that resolves to the updated document with metadata
* @mermaid
* sequenceDiagram
* participant Client
* participant PouchAdapter
* participant PouchDB
*
* Client->>PouchAdapter: update(tableName, id, model)
* PouchAdapter->>PouchDB: put(model)
* alt Success
* PouchDB-->>PouchAdapter: Response with ok=true
* PouchAdapter->>PouchAdapter: assignMetadata(model, response.rev)
* PouchAdapter-->>Client: Updated model with metadata
* else Error
* PouchDB-->>PouchAdapter: Error
* PouchAdapter->>PouchAdapter: parseError(e)
* PouchAdapter-->>Client: Throws error
* end
*/
override async update(
tableName: string,
id: string | number,
model: Record<string, any>
): Promise<Record<string, any>> {
let response: Response;
try {
response = await this.client.put(model);
} catch (e: any) {
throw PouchAdapter.parseError(e);
}
if (!response.ok)
throw new InternalError(
`Failed to update doc id: ${id} in table ${tableName}`
);
return this.assignMetadata(model, response.rev);
}
/**
* @description Updates multiple documents in the database in a single operation
* @summary Updates multiple documents in the PouchDB database using the bulkDocs operation.
* This method handles error parsing and ensures all operations were successful.
* @param {string} tableName - The name of the table/collection
* @param {string[]|number[]} ids - The document IDs
* @param models - The updated document data
* @return A promise that resolves to the updated documents with metadata
* @mermaid
* sequenceDiagram
* participant Client
* participant PouchAdapter
* participant PouchDB
*
* Client->>PouchAdapter: updateAll(tableName, ids, models)
* PouchAdapter->>PouchDB: bulkDocs(models)
* alt Success
* PouchDB-->>PouchAdapter: Array of responses with ok=true
* PouchAdapter->>PouchAdapter: assignMultipleMetadata(models, revs)
* PouchAdapter-->>Client: Updated models with metadata
* else Error
* PouchDB-->>PouchAdapter: Array with errors
* PouchAdapter->>PouchAdapter: Check for errors
* PouchAdapter-->>Client: Throws InternalError
* end
*/
override async updateAll(
tableName: string,
ids: string[] | number[],
models: Record<string, any>[]
): Promise<Record<string, any>[]> {
let response: (Response | Err)[];
try {
response = await this.client.bulkDocs(models);
} catch (e: any) {
throw PouchAdapter.parseError(e);
}
if (!response.every((r) => !(r as any).error)) {
const errors = response.reduce((accum: string[], el, i) => {
if ((el as any).error)
accum.push(
`el ${i}: ${(el as any).error}${(el as any).reason ? ` - ${(el as any).reason}` : ""}`
);
return accum;
}, []);
throw new InternalError(errors.join("\n"));
}
return this.assignMultipleMetadata(
models,
response.map((r) => r.rev as string)
);
}
/**
* @description Deletes a document from the database by ID
* @summary Removes a document from the PouchDB database using the remove operation.
* This method first retrieves the document to get its revision, then deletes it.
* @param {string} tableName - The name of the table/collection
* @param {string|number} id - The document ID
* @return {Promise<Record<string, any>>} A promise that resolves to the deleted document with metadata
* @mermaid
* sequenceDiagram
* participant Client
* participant PouchAdapter
* participant PouchDB
*
* Client->>PouchAdapter: delete(tableName, id)
* PouchAdapter->>PouchAdapter: generateId(tableName, id)
* PouchAdapter->>PouchDB: get(_id)
* PouchDB-->>PouchAdapter: Document with _rev
* PouchAdapter->>PouchDB: remove(_id, record._rev)
* alt Success
* PouchDB-->>PouchAdapter: Success response
* PouchAdapter->>PouchAdapter: assignMetadata(record, record._rev)
* PouchAdapter-->>Client: Deleted document with metadata
* else Error
* PouchDB-->>PouchAdapter: Error
* PouchAdapter->>PouchAdapter: parseError(e)
* PouchAdapter-->>Client: Throws error
* end
*/
override async delete(
tableName: string,
id: string | number
): Promise<Record<string, any>> {
const _id = this.generateId(tableName, id);
let record: IdMeta & GetMeta;
try {
record = await this.client.get(_id);
await this.client.remove(_id, record._rev);
} catch (e: any) {
throw PouchAdapter.parseError(e);
}
return this.assignMetadata(record, record._rev);
}
/**
* @description Deletes multiple documents from the database by their IDs
* @summary Removes multiple documents from the PouchDB database in a single operation.
* This method first retrieves all documents to get their revisions, then marks them as deleted.
* @param {string} tableName - The name of the table/collection
* @param {Array<string|number|bigint>} ids - The document IDs
* @return A promise that resolves to the deleted documents with metadata
* @mermaid
* sequenceDiagram
* participant Client
* participant PouchAdapter
* participant PouchDB
*
* Client->>PouchAdapter: deleteAll(tableName, ids)
* PouchAdapter->>PouchAdapter: Map ids to generateId(tableName, id)
* PouchAdapter->>PouchDB: bulkGet({docs})
* PouchDB-->>PouchAdapter: BulkGetResponse with documents
* PouchAdapter->>PouchAdapter: Mark documents as deleted
* PouchAdapter->>PouchDB: bulkDocs(marked documents)
* alt Success
* PouchDB-->>PouchAdapter: Success responses
* PouchAdapter->>PouchAdapter: Process results
* PouchAdapter->>PouchAdapter: assignMetadata for each doc
* PouchAdapter-->>Client: Deleted documents with metadata
* else Error
* PouchAdapter->>PouchAdapter: Check for errors
* PouchAdapter-->>Client: Throws InternalError
* end
*/
override async deleteAll(
tableName: string,
ids: (string | number | bigint)[]
): Promise<Record<string, any>[]> {
const results: BulkGetResponse<any> = await this.client.bulkGet({
docs: ids.map((id) => ({ id: this.generateId(tableName, id as any) })),
});
const deletion: (Response | Err)[] = await this.client.bulkDocs(
results.results.map((r) => {
(r as any)[CouchDBKeys.DELETED] = true;
return r;
})
);
const errs = deletion.filter((d) => (d as any).error);
if (errs.length) throw new InternalError(errs.join("\n"));
return results.results.reduce((accum: any[], r) => {
r.docs.forEach((d) => {
const result = Object.assign({}, (d as { ok: any }).ok);
accum.push(this.assignMetadata(result, (d as any).ok[CouchDBKeys.REV]));
});
return accum;
}, []);
}
/**
* @description Executes a raw Mango query against the database
* @summary Performs a direct find operation using a Mango query object.
* This method allows for complex queries beyond the standard CRUD operations.
* @template V - The return type
* @param {MangoQuery} rawInput - The Mango query to execute
* @param {boolean} [process=true] - Whether to process the response (true returns just docs, false returns full response)
* @return {Promise<V>} A promise that resolves to the query results
* @mermaid
* sequenceDiagram
* participant Client
* participant PouchAdapter
* participant PouchDB
*
* Client->>PouchAdapter: raw<V>(rawInput, process)
* PouchAdapter->>PouchDB: find(rawInput)
* alt Success
* PouchDB-->>PouchAdapter: FindResponse
* alt process=true
* PouchAdapter-->>Client: response.docs as V
* else process=false
* PouchAdapter-->>Client: response as V
* end
* else Error
* PouchDB-->>PouchAdapter: Error
* PouchAdapter->>PouchAdapter: parseError(e)
* PouchAdapter-->>Client: Throws error
* end
*/
async raw<V>(rawInput: MangoQuery, process = true): Promise<V> {
try {
const response: FindResponse<any> = await this.client.find(
rawInput as any
);
if (response.warning) console.warn(response.warning);
if (process) return response.docs as V;
return response as V;
} catch (e: any) {
throw PouchAdapter.parseError(e);
}
}
/**
* @description Parses and converts errors from PouchDB to application-specific errors
* @summary Converts PouchDB errors to the application's error hierarchy.
* This instance method delegates to the static parseError method.
* @param {Error|string} err - The error object or message to parse
* @param {string} [reason] - Optional reason for the error
* @return {BaseError} The converted error object
*/
override parseError(err: Error | string, reason?: string): BaseError {
return PouchAdapter.parseError(err, reason);
}
/**
* @description Static method to parse and convert errors from PouchDB to application-specific errors
* @summary Converts PouchDB errors to the application's error hierarchy based on error codes and messages.
* This method analyzes the error type, status code, or message to determine the appropriate error class.
* @param {Error|string} err - The error object or message to parse
* @param {string} [reason] - Optional reason for the error
* @return {BaseError} The converted error object
* @mermaid
* sequenceDiagram
* participant Caller
* participant PouchAdapter
*
* Caller->>PouchAdapter: parseError(err, reason)
* alt err is BaseError
* PouchAdapter-->>Caller: Return err as is
* else err is string
* alt contains "already exist" or "update conflict"
* PouchAdapter-->>Caller: ConflictError
* else contains "missing" or "deleted"
* PouchAdapter-->>Caller: NotFoundError
* end
* else err has status
* alt status is 401, 412, 409
* PouchAdapter-->>Caller: ConflictError
* else status is 404
* PouchAdapter-->>Caller: NotFoundError
* else status is 400
* alt message contains "No index exists"
* PouchAdapter-->>Caller: IndexError
* else
* PouchAdapter-->>Caller: InternalError
* end
* else message contains "ECONNREFUSED"
* PouchAdapter-->>Caller: ConnectionError
* else
* PouchAdapter-->>Caller: InternalError
* end
* end
*/
static override parseError(err: Error | string, reason?: string): BaseError {
// return super.parseError(err, reason);
if (err instanceof BaseError) return err as any;
let code: string = "";
if (typeof err === "string") {
code = err;
if (code.match(/already exist|update conflict/g))
return new ConflictError(code);
if (code.match(/missing|deleted/g)) return new NotFoundError(code);
} else if ((err as any).status) {
code = (err as any).status;
reason = reason || err.message;
} else {
code = err.message;
}
switch (code.toString()) {
case "401":
case "412":
case "409":
return new ConflictError(reason as string);
case "404":
return new NotFoundError(reason as string);
case "400":
if (code.toString().match(/No\sindex\sexists/g))
return new IndexError(err);
return new InternalError(err);
default:
if (code.toString().match(/ECONNREFUSED/g))
return new ConnectionError(err);
return new InternalError(err);
}
}
/**
* @description Sets up decorations for PouchDB-specific model properties
* @summary Configures decorators for createdBy and updatedBy fields in models.
* This method defines how these fields should be automatically populated during create and update operations.
* @mermaid
* sequenceDiagram
* participant Caller
* participant PouchAdapter
* participant Decoration
*
* Caller->>PouchAdapter: decoration()
* PouchAdapter->>Repository: key(PersistenceKeys.CREATED_BY)
* Repository-->>PouchAdapter: createdByKey
* PouchAdapter->>Repository: key(PersistenceKeys.UPDATED_BY)
* Repository-->>PouchAdapter: updatedByKey
*
* PouchAdapter->>Decoration: flavouredAs(PouchFlavour)
* Decoration-->>PouchAdapter: DecoratorBuilder
* PouchAdapter->>Decoration: for(createdByKey)
* PouchAdapter->>Decoration: define(onCreate, propMetadata)
* PouchAdapter->>Decoration: apply()
*
* PouchAdapter->>Decoration: flavouredAs(PouchFlavour)
* Decoration-->>PouchAdapter: DecoratorBuilder
* PouchAdapter->>Decoration: for(updatedByKey)
* PouchAdapter->>Decoration: define(onCreate, propMetadata)
* PouchAdapter->>Decoration: apply()
*/
static override decoration() {
super.decoration();
const createdByKey = Repository.key(PersistenceKeys.CREATED_BY);
const updatedByKey = Repository.key(PersistenceKeys.UPDATED_BY);
Decoration.flavouredAs(PouchFlavour)
.for(createdByKey)
.define(
onCreate(createdByOnPouchCreateUpdate),
propMetadata(createdByKey, {})
)
.apply();
Decoration.flavouredAs(PouchFlavour)
.for(updatedByKey)
.define(
onCreateUpdate(createdByOnPouchCreateUpdate),
propMetadata(updatedByKey, {})
)
.apply();
}
}
PouchAdapter.setCurrent(PouchFlavour);
Source