@temboplus/afloat 0.1.70 → 0.1.72

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/lib/error/error.api.ts","../src/models/permission.ts","../src/lib/error/error.permission.ts","../src/features/contact/contact.dtos.ts","../src/models/contact-info.model.ts","../src/models/contact.model.ts","../src/features/payout/payout.dtos.ts","../src/models/payout.model.ts","../src/features/auth/profile/profile.dtos.ts","../src/models/profile.model.ts","../src/models/user.model.ts","../src/features/wallet/wallet.dtos.ts","../src/models/wallet.model.ts","../src/models/narration.model.ts","../src/models/statement-entry.model.ts","../src/models/role.model.ts","../src/models/managed-user.model.ts","../node_modules/@ts-rest/core/index.esm.mjs","../node_modules/zustand/esm/middleware.mjs","../src/features/auth/auth.store.ts","../src/lib/api/base-repository.ts","../src/features/auth/auth.contract.ts","../src/features/auth/identity/identity.api-contract.ts","../src/lib/api/common-responses.ts","../src/features/auth/identity/identity.repository.ts","../src/features/auth/auth.repository.ts","../src/features/wallet/wallet.utils.ts","../src/features/wallet/wallet.contract.ts","../src/features/wallet/wallet.repository.ts","../src/features/wallet/wallet-manager.session.ts","../src/features/auth/auth.manager.ts","../src/features/auth/access/access.api-contract.ts","../src/features/auth/profile/profile.api-contract.ts","../src/features/contact/contact.api-contract.ts","../src/features/contact/contact-input-handler.ts","../src/features/payout/payout.api-contract.ts","../src/features/payout/payout-channel-handler.ts","../src/features/admin/admin.dtos.ts","../src/features/admin/admin.contract.ts","../src/features/auth/access/access.repository.ts","../src/features/contact/contact.repository.ts","../src/features/payout/payout.repository.ts","../src/features/auth/profile/profile.repository.ts","../src/features/admin/admin.repository.ts","../src/lib/error/error.utils.ts"],"sourcesContent":["import { z } from \"zod\";\n\n/**\n * Custom error class representing API-related errors.\n * Extends the built-in `Error` class to include additional properties such as `statusCode`, `error`, and `details`.\n */\nexport class APIError extends Error {\n /**\n * The HTTP status code associated with the error.\n * @type {number}\n */\n public readonly statusCode: number;\n\n /**\n * An optional error identifier or code.\n * @type {string | undefined}\n */\n public readonly error?: string;\n\n /**\n * Additional details about the error, often used for debugging purposes.\n * @type {Record<string, unknown> | undefined}\n */\n public readonly details?: Record<string, unknown>;\n\n /**\n * Creates a new `APIError` instance.\n * @param {Object} args - The arguments to initialize the error.\n * @param {string} args.message - The error message.\n * @param {number} args.statusCode - The HTTP status code associated with the error.\n * @param {string} [args.error] - An optional error identifier or code.\n * @param {Record<string, unknown>} [args.details] - Additional details about the error.\n */\n constructor(args: {\n message: string;\n statusCode: number;\n error?: string;\n details?: Record<string, unknown>;\n }) {\n super(args.message);\n this.name = \"ApiError\";\n\n this.statusCode = args.statusCode;\n if (this.error) this.error = args.error;\n if (args.details) this.details = args.details;\n }\n\n /**\n * Validates whether an unknown value conforms to the APIError schema.\n * This is more thorough than an instanceof check as it verifies all required properties\n * and their types using the defined schema.\n *\n * @param {unknown} error - Any value to be validated\n * @returns {error is APIError} Type predicate indicating if the value is a valid APIError\n *\n * @example\n * try {\n * throw new Error('Network failed');\n * } catch (err) {\n * if (APIError.is(err)) {\n * // err is typed as APIError here\n * console.log(err.statusCode);\n * }\n * }\n */\n public static is(error: unknown): error is APIError {\n const result = APIError.schema.safeParse(error);\n return result.success;\n }\n\n public static unknown(message?: string): APIError {\n return new APIError({\n message: message ?? \"An unknown error occurred\",\n statusCode: 502,\n });\n }\n\n public static get schema(): z.ZodObject<{\n message: z.ZodString;\n statusCode: z.ZodNumber;\n error: z.ZodOptional<z.ZodString>;\n details: z.ZodOptional<z.AnyZodObject>;\n }> {\n return z.object({\n message: z.string(),\n statusCode: z.number().int(),\n error: z.string().optional(),\n details: z.object({}).optional(),\n });\n }\n}\n","/**\n * All Afloat Permissions\n */\nexport const Permissions = {\n Profile: {\n ViewCurrent: \"profile.getCurrent\",\n Update: \"profile.update\",\n },\n Contact: {\n View: \"contact.findById\",\n List: \"contact.findAll\",\n Create: \"contact.create\",\n Update: \"contact.update\",\n Delete: \"contact.delete\",\n },\n Payment: {\n View: \"payment.findById\",\n List: \"payment.findAll\",\n Create: \"payment.create\",\n },\n Payout: {\n View: \"payout.findById\",\n List: \"payout.findAll\",\n Create: \"payout.create\",\n Approve: \"payout.approve\",\n },\n Transfer: {\n View: \"transfer.findById\",\n List: \"transfer.findAll\",\n Create: \"transfer.create\",\n Approve: \"transfer.approve\",\n },\n Wallet: {\n ViewBalance: \"wallet.getBalance\",\n ViewStatement: \"wallet.getStatement\",\n },\n Role: {\n ViewRoles: \"role.findAll\",\n ViewRole: \"role.findById\",\n },\n UserManagement: {\n ViewUsers: \"login.findAll\",\n ViewUser: \"login.findById\",\n CreateUser: \"login.create\",\n UpdateUser: \"login.update\",\n ArchiveUser: \"login.archive\",\n UnArchiveUser: \"login.unarchive\",\n ResetPassword: \"login.resetPassword\",\n },\n} as const; // Make it deeply readonly\n\n/**\n * Permission Type\n */\nexport type Permission =\n | (typeof Permissions.Profile)[keyof typeof Permissions.Profile]\n | (typeof Permissions.Contact)[keyof typeof Permissions.Contact]\n | (typeof Permissions.Payment)[keyof typeof Permissions.Payment]\n | (typeof Permissions.Payout)[keyof typeof Permissions.Payout]\n | (typeof Permissions.Transfer)[keyof typeof Permissions.Transfer]\n | (typeof Permissions.UserManagement)[keyof typeof Permissions.UserManagement]\n | (typeof Permissions.Role)[keyof typeof Permissions.Role]\n | (typeof Permissions.Wallet)[keyof typeof Permissions.Wallet];\n","import { Permissions, Permission } from \"@/models/permission\";\nimport { z } from \"zod\";\n\n/**\n * Custom error class representing an error caused by missing required permissions.\n * Extends the built-in {@link Error} class to include the `requiredPermissions` property.\n */\nexport class PermissionError extends Error {\n /**\n * The permissions that are required but were not present, causing the error.\n * @type {Permission[]}\n */\n public readonly requiredPermissions: Permission[];\n\n /**\n * Creates a new `PermissionError` instance.\n * @param {Object} args - The constructor arguments.\n * @param {Permission[]} args.requiredPermissions - An array of permissions required for the operation.\n * @param {string} [args.message] - An optional custom error message. Defaults to listing the missing permissions.\n */\n constructor(args: { requiredPermissions: Permission[]; message?: string }) {\n super(args.message ?? `Missing required permissions: ${args.requiredPermissions.join(\", \")}`);\n this.name = \"PermissionError\";\n this.requiredPermissions = args.requiredPermissions;\n }\n\n /**\n * Validates if an unknown value is a valid PermissionError instance.\n * Performs structural validation of the error object and its properties.\n *\n * @param {unknown} error - The value to validate.\n * @returns {error is PermissionError} Type predicate indicating if the value is a valid PermissionError.\n *\n * @example\n * try {\n * throw new Error('Access denied');\n * } catch (error) {\n * if (PermissionError.is(error)) {\n * // error is typed as PermissionError with properly typed requiredPermissions\n * console.log(error.requiredPermissions);\n * }\n * }\n *\n * @remarks\n * Validates the following:\n * - Has all required Error properties\n * - Has correct error name\n * - Contains properly structured requiredPermissions array\n * - Maintains proper prototype chain\n */\n public static is(error: unknown): error is PermissionError {\n const permissionSchema = z.union([\n z.enum(Object.values(Permissions.Profile) as [string, ...string[]]),\n z.enum(Object.values(Permissions.Contact) as [string, ...string[]]),\n z.enum(Object.values(Permissions.Payment) as [string, ...string[]]),\n z.enum(Object.values(Permissions.Payout) as [string, ...string[]]),\n z.enum(Object.values(Permissions.Transfer) as [string, ...string[]]),\n z.enum(Object.values(Permissions.Wallet) as [string, ...string[]]),\n ]);\n\n const errorSchema = z.object({\n name: z.literal(\"PermissionError\"),\n message: z.string(),\n requiredPermissions: z.array(permissionSchema),\n });\n\n return errorSchema.safeParse(error).success;\n }\n}\n","import { z } from \"zod\";\n\n/**\n * Schema for contact channel types.\n *\n * @remarks\n * Currently supports two channel types:\n * - \"Bank\": For traditional banking channels\n * - \"Mobile\": For mobile money channels\n *\n * @see {@link ContactType} for the inferred type\n */\nexport enum ContactType {\n Bank = \"Bank\",\n Mobile = \"Mobile\",\n}\n\nconst contactTypeSchema = z.nativeEnum(ContactType);\n\n/**\n * Type representing user-provided contact information.\n * Used for creating or updating contacts.\n *\n * @see {@link contactInputSchema} for validation rules\n * @see {@link ContactDTOSchemas} for all available schemas\n */\nexport type ContactInputDTO = z.infer<typeof ContactDTOSchemas.contactInputDTO>;\n\n/**\n * Schema for validating contact input data.\n *\n * @remarks\n * Validates the following fields:\n * - displayName: Non-empty string\n * - accountNo: Non-empty string\n * - channel: Non-empty string\n * - type: Must be either \"Bank\" or \"Mobile\"\n */\nconst contactInputSchema = z.object({\n displayName: z.string().min(1, \"Display name is required\"),\n accountNo: z.string().min(1, \"Account number is required\"),\n channel: z.string().min(1, \"Channel is required\"),\n type: contactTypeSchema,\n});\n\n/**\n * Schema for complete contact records.\n *\n * @remarks\n * Extends contactInputSchema with additional system fields:\n * - id: Unique identifier\n * - profileId: Associated profile ID\n * - createdAt: Creation timestamp\n * - updatedAt: Last update timestamp\n */\nconst contactSchema = z\n .object({\n id: z.string().min(1, \"Contact id is required\"),\n profileId: z.string(),\n createdAt: z.string().datetime(),\n updatedAt: z.string().datetime(),\n })\n .merge(contactInputSchema);\n\n/**\n * Type representing a complete contact record.\n * Includes both user-provided and system-generated fields.\n *\n * @see {@link contactSchema} for validation rules\n */\nexport type ContactDTO = z.infer<typeof ContactDTOSchemas.contactDTO>;\n\n/**\n * Collection of all contact-related schemas.\n */\nexport const ContactDTOSchemas = {\n /** Schema for complete contact records */\n contactDTO: contactSchema,\n /** Schema for contact input validation */\n contactInputDTO: contactInputSchema,\n /** Schema for contact channel types */\n contactType: contactTypeSchema,\n};\n","import {\n Bank,\n BankValidation,\n CountryValidation,\n ISO2CountryCode,\n MNOUtils,\n PhoneNumber,\n PhoneNumberFormat,\n} from \"@temboplus/frontend-core\";\nimport { PayoutDTO } from \"../features/payout\";\nimport { ContactType, ContactDTO } from \"../features/contact/contact.dtos\";\n\nimport type { BankSwiftCode, MNOId } from \"@temboplus/frontend-core\";\n\n/**\n * Custom error class for contact information validation errors.\n * Provides structured context information for better debugging and error handling.\n *\n * @class ContactInfoError\n * @extends Error\n *\n * @example\n * ```typescript\n * try {\n * const contact = new MobileContactInfo(\"\", phoneNumber);\n * } catch (error) {\n * if (error instanceof ContactInfoError) {\n * console.log(\"Operation:\", error.context.operation);\n * console.log(\"Phone:\", error.context.phoneNumber);\n * console.log(\"Country:\", error.context.countryCode);\n * }\n * }\n * ```\n */\nclass ContactInfoError extends Error {\n /**\n * Creates a new ContactInfoError with structured context information.\n *\n * @param {string} message - The error message describing what went wrong\n * @param {Object} [context={}] - Additional context information for debugging\n * @param {string} [context.phoneNumber] - The phone number that caused the error (E164 format)\n * @param {ISO2CountryCode} [context.countryCode] - The country code involved in the error\n * @param {string} [context.mnoId] - The MNO ID that caused validation issues\n * @param {string} [context.operation] - The operation being performed when error occurred\n */\n constructor(\n message: string,\n public readonly context: {\n phoneNumber?: string;\n countryCode?: ISO2CountryCode;\n mnoId?: string;\n operation?: string;\n } = {}\n ) {\n super(message);\n this.name = \"ContactInfoError\";\n }\n}\n\n/**\n * Abstract base class that provides a common interface for different types of contact information.\n * This class defines the structure and validation requirements for both mobile and bank contacts.\n *\n * Serves as the foundation for type-safe contact handling throughout the application,\n * ensuring consistent behavior between mobile money and bank transfer contacts.\n *\n * **Country Code Handling:**\n * The country code is always derived from the underlying domain objects (PhoneNumber or Bank)\n * rather than being passed as a separate parameter, ensuring consistency and single source of truth.\n *\n * @abstract\n * @class BaseContactInfo\n *\n * @property {ContactType} type - The type of contact (either \"Mobile\" or \"Bank\")\n * @property {ISO2CountryCode} countryCode - The ISO2 country code derived from PhoneNumber or Bank\n *\n * @example\n * ```typescript\n * // Cannot instantiate directly - use concrete implementations\n * const mobileContact = new MobileContactInfo(\"John Doe\", phoneNumber);\n * const bankContact = new BankContactInfo(\"Jane Smith\", bank, \"123456789\");\n *\n * // Polymorphic usage\n * function processContact(contact: BaseContactInfo) {\n * console.log(`Processing ${contact.type} contact: ${contact.displayName}`);\n * console.log(`Channel: ${contact.channelDisplayName}`);\n * console.log(`Country: ${contact.countryCode}`); // Always available\n * }\n * ```\n */\nabstract class BaseContactInfo {\n public readonly type: ContactType;\n public readonly countryCode: ISO2CountryCode;\n\n /**\n * Creates a new instance of BaseContactInfo.\n * Protected constructor ensures only subclasses can instantiate.\n *\n * @protected\n * @param {ContactType} type - The type of contact to create (\"Mobile\" or \"Bank\")\n * @param {ISO2CountryCode} countryCode - The ISO2 country code derived from domain objects\n */\n constructor(type: ContactType, countryCode: ISO2CountryCode) {\n this.type = type;\n this.countryCode = countryCode;\n }\n\n /**\n * Gets the typed channel identifier for the contact.\n * Returns MNO ID for mobile contacts, SWIFT code for bank contacts.\n *\n * @abstract\n * @returns {MNOId | BankSwiftCode} The typed channel identifier\n *\n * @example\n * ```typescript\n * const mobileChannel: MNOId = mobileContact.channelId; // \"VODACOM\", \"SAFARICOM\", etc.\n * const bankChannel: BankSwiftCode = bankContact.channelId; // \"CORUTZTZ\", \"KCBLKENX\", etc.\n * ```\n */\n abstract get channelId(): MNOId | BankSwiftCode;\n\n /**\n * Gets the human-readable channel name for display purposes.\n * Returns mobile money service name for mobile contacts, bank short name for bank contacts.\n *\n * @abstract\n * @returns {string} The display-friendly channel name\n *\n * @example\n * ```typescript\n * console.log(mobileContact.channelName); // \"M-Pesa\", \"Airtel Money\", etc.\n * console.log(bankContact.channelName); // \"CRDB\", \"KCB\", etc.\n * ```\n */\n abstract get channelName(): string;\n\n /**\n * Gets the primary display name for the contact.\n *\n * @abstract\n * @returns {string} The contact's display name (personal name for mobile, account name for bank)\n */\n abstract get accountName(): string;\n\n /**\n * Gets the primary account/identification number for the contact.\n *\n * @abstract\n * @returns {string} The contact's number (phone number for mobile, account number for bank)\n */\n abstract get accountNumber(): string;\n\n /**\n * Gets the localized label for the display name field.\n *\n * @abstract\n * @returns {string} The appropriate label for the display name based on contact type\n */\n abstract get accountNameLabel(): string;\n\n /**\n * Gets the localized label for the account number field.\n *\n * @abstract\n * @returns {string} The appropriate label for the account number based on contact type\n */\n abstract get accountNumberLabel(): string;\n\n /**\n * Gets the localized label for the channel field.\n *\n * @abstract\n * @returns {string} The appropriate label for the channel based on contact type\n */\n abstract get channelLabel(): string;\n\n /**\n * Validates that all contact information is consistent and correct.\n *\n * @abstract\n * @returns {boolean} True if the contact information is valid\n */\n abstract validate(): boolean;\n}\n\n/**\n * Implementation of BaseContactInfo for mobile phone contacts.\n * Handles storage, validation, and display of contact details specific to mobile money services.\n *\n * This class properly delegates MNO-related logic to country-specific implementations,\n * supporting both countries with Mobile Number Portability (MNP) and those without.\n * The country code is always derived from the PhoneNumber object, ensuring consistency.\n *\n * **Key Features:**\n * - Automatic MNO detection for countries without MNP (e.g., Tanzania)\n * - Explicit MNO requirement for countries with MNP (e.g., Kenya)\n * - Country-specific validation through MNOUtils delegation\n * - Comprehensive error handling with structured context\n * - Country code derived from PhoneNumber (single source of truth)\n *\n * **MNP Handling:**\n * - **Tanzania (no MNP)**: MNO is always auto-detected from phone number prefix, ignoring any provided MNO\n * - **Kenya (has MNP)**: MNO must be explicitly provided as numbers can be ported\n *\n * @extends BaseContactInfo\n * @class MobileContactInfo\n *\n * @property {string} name - The contact's personal name\n * @property {PhoneNumber} phoneNumber - The validated phone number object\n * @property {MNOId} mnoId - The mobile network operator identifier (always available after construction)\n *\n * @example\n * ```typescript\n * // Tanzania - MNO auto-detected from prefix (ignores provided MNO)\n * const tzContact = new MobileContactInfo(\"John Doe\", tzPhoneNumber);\n * console.log(tzContact.channel); // \"VODACOM\" (auto-detected)\n * console.log(tzContact.countryCode); // \"TZ\" (from phoneNumber)\n *\n * // Tanzania - Even with explicit MNO, it's auto-detected from prefix\n * const tzExplicit = new MobileContactInfo(\"John Doe\", tzPhoneNumber, TZMNOId.AIRTEL);\n * console.log(tzExplicit.channel); // \"VODACOM\" (auto-detected, not AIRTEL)\n *\n * // Kenya - MNO must be explicit due to MNP\n * const keContact = new MobileContactInfo(\"Jane Smith\", kePhoneNumber, KEMNOId.SAFARICOM);\n * console.log(keContact.countryCode); // \"KE\" (from phoneNumber)\n *\n * // Kenya - This would throw an error (MNO required)\n * // const keContact = new MobileContactInfo(\"Jane Smith\", kePhoneNumber); // ❌ Error\n * ```\n *\n * @throws {ContactInfoError} When validation fails (invalid name, phone, or MNO)\n */\nexport class MobileContactInfo extends BaseContactInfo {\n public readonly mnoId: MNOId;\n\n /**\n * Creates a new mobile contact with comprehensive validation.\n *\n * **Validation Process:**\n * 1. Validates name is non-empty\n * 2. Validates phone number structure\n * 3. Extracts country code from PhoneNumber object\n * 4. Handles MNO validation based on country MNP status:\n * - **Countries with MNP (KE)**: Requires explicit MNO, validates it's valid for country\n * - **Countries without MNP (TZ)**: Always auto-detects MNO from phone number prefix (ignores provided MNO)\n *\n * @param {string} name - The contact's personal name (required, non-empty)\n * @param {PhoneNumber} phoneNumber - The validated phone number object (contains country code)\n * @param {MNOId} [mnoId] - MNO ID. Required for MNP countries (KE), ignored for non-MNP countries (TZ)\n *\n * @throws {ContactInfoError} When any validation fails:\n * - Empty or invalid name\n * - Invalid phone number structure\n * - Invalid MNO for the country (MNP countries only)\n * - Missing MNO for MNP countries\n * - Unable to auto-detect MNO for non-MNP countries\n *\n * @example\n * ```typescript\n * try {\n * // Tanzania - MNO always auto-detected\n * const tzAuto = new MobileContactInfo(\"John\", tzPhone);\n * const tzIgnored = new MobileContactInfo(\"John\", tzPhone, TZMNOId.AIRTEL); // AIRTEL ignored, auto-detected from prefix\n *\n * // Kenya - MNO must be explicit\n * const keExplicit = new MobileContactInfo(\"Jane\", kePhone, KEMNOId.SAFARICOM);\n * } catch (error) {\n * if (error instanceof ContactInfoError) {\n * console.log(`${error.message} - Context:`, error.context);\n * }\n * }\n * ```\n */\n constructor(public readonly name: string, public readonly phoneNumber: PhoneNumber, mnoId?: MNOId) {\n super(ContactType.Mobile, phoneNumber.countryCode);\n\n // Validate name\n if (!name?.trim()) {\n throw new ContactInfoError(\"Name is required and cannot be empty\", {\n operation: \"constructor\",\n countryCode: phoneNumber.countryCode,\n phoneNumber: phoneNumber.e164Format,\n });\n }\n\n // Validate phone number\n if (!phoneNumber?.validate()) {\n throw new ContactInfoError(\"Invalid phone number\", {\n operation: \"constructor\",\n countryCode: phoneNumber.countryCode,\n phoneNumber: phoneNumber.e164Format,\n });\n }\n\n // Handle MNO logic based on country MNP status\n if (MNOUtils.requiresExplicitMNO(phoneNumber.countryCode)) {\n // MNP countries (e.g., Kenya) - require explicit MNO\n if (!mnoId) {\n throw new ContactInfoError(\n `MNO must be explicitly provided for phone numbers in ${phoneNumber.countryCode} due to Mobile Number Portability`,\n {\n phoneNumber: phoneNumber.e164Format,\n countryCode: phoneNumber.countryCode,\n operation: \"constructor\",\n }\n );\n }\n\n // Validate the provided MNO is valid for the country\n if (!MNOUtils.isValidMNOForCountry(mnoId, phoneNumber.countryCode)) {\n const validMNOs = MNOUtils.getCountryMNOs(phoneNumber.countryCode).map((mno) => mno.id);\n throw new ContactInfoError(\n `Invalid MNO ${mnoId} for country ${phoneNumber.countryCode}. Valid MNOs: ${validMNOs.join(\", \")}`,\n {\n phoneNumber: phoneNumber.e164Format,\n countryCode: phoneNumber.countryCode,\n mnoId,\n operation: \"constructor\",\n }\n );\n }\n\n this.mnoId = mnoId;\n } else {\n // Non-MNP countries (e.g., Tanzania) - always auto-detect from phone number\n // Any provided MNO is ignored\n const mno = MNOUtils.getMNOByPhoneNumber(phoneNumber.e164Format, phoneNumber.countryCode);\n if (!mno?.id) {\n throw new ContactInfoError(`Failed to determine MNO for phone number ${phoneNumber.e164Format}`, {\n phoneNumber: phoneNumber.e164Format,\n countryCode: phoneNumber.countryCode,\n operation: \"constructor\",\n });\n }\n\n this.mnoId = mno.id;\n }\n }\n\n /**\n * Creates a MobileContactInfo instance from a ContactDTO object.\n * Handles validation and MNO extraction from the DTO's channel field.\n * The country code is automatically derived from the parsed phone number.\n *\n * **Process:**\n * 1. Validates DTO type is \"Mobile\"\n * 2. Parses phone number (extracts country code from E164 format)\n * 3. Handles MNO based on country MNP status:\n * - **MNP countries**: Uses channel field (required)\n * - **Non-MNP countries**: Auto-detects from phone number (ignores channel field)\n * 4. Delegates to constructor for final validation\n *\n * @static\n * @param {ContactDTO} info - The contact data transfer object\n * @param {string} info.type - Must be \"Mobile\"\n * @param {string} info.accountNo - The phone number string to parse (should be E164 format)\n * @param {string} info.displayName - The contact's name\n * @param {string} [info.channel] - MNO ID (required for MNP countries, ignored for non-MNP countries)\n *\n * @returns {MobileContactInfo | undefined} New instance if successful, undefined if validation fails\n *\n * @example\n * ```typescript\n * // Tanzania DTO - channel ignored, auto-detected from phone number\n * const tzContactDTO = {\n * type: \"Mobile\",\n * accountNo: \"+255712345678\", // Vodacom prefix\n * displayName: \"John Doe\",\n * channel: \"AIRTEL\" // This will be ignored, Vodacom will be auto-detected\n * };\n *\n * // Kenya DTO - channel required\n * const keContactDTO = {\n * type: \"Mobile\",\n * accountNo: \"+254712345678\",\n * displayName: \"Jane Smith\",\n * channel: \"SAFARICOM\" // Required for Kenya\n * };\n *\n * const tzContact = MobileContactInfo.fromContactDTO(tzContactDTO);\n * console.log(tzContact?.channelId); // \"VODACOM\" (not \"AIRTEL\")\n * console.log(tzContact?.countryCode); // \"TZ\" (from phone number)\n *\n * const keContact = MobileContactInfo.fromContactDTO(keContactDTO);\n * console.log(keContact?.channelId); // \"SAFARICOM\"\n * console.log(keContact?.countryCode); // \"KE\" (from phone number)\n * ```\n */\n public static fromContactDTO(info: ContactDTO): MobileContactInfo | undefined {\n if (info.type !== \"Mobile\") return undefined;\n\n let phoneNumberString = info.accountNo;\n if (!phoneNumberString.startsWith(\"+\")) phoneNumberString = `+${phoneNumberString}`;\n\n const phone = PhoneNumber.from(phoneNumberString);\n if (!phone) {\n console.error(`Failed to parse phone number ${phoneNumberString}`);\n return undefined;\n }\n\n const countryCode = phone.countryCode;\n\n try {\n let mnoId: MNOId | undefined;\n\n if (MNOUtils.requiresExplicitMNO(countryCode)) {\n // MNP countries - use channel field (required)\n if (info.channel && typeof info.channel === \"string\") {\n if (MNOUtils.isValidMNOForCountry(info.channel, countryCode)) {\n mnoId = info.channel as MNOId;\n } else {\n console.warn(`Invalid MNO ${info.channel} for country ${countryCode}`);\n return undefined;\n }\n }\n // If no valid channel provided for MNP country, constructor will throw error\n } else {\n // Non-MNP countries - always auto-detect (ignore channel field)\n mnoId = MNOUtils.getMNOByPhoneNumber(phone.e164Format, countryCode)?.id;\n if (!mnoId) {\n console.warn(`Failed to auto-detect MNO for phone number ${phone.e164Format}`);\n return undefined;\n }\n }\n\n return new MobileContactInfo(info.displayName, phone, mnoId);\n } catch (error) {\n console.error(`Failed to create MobileContactInfo: ${error}`);\n return undefined;\n }\n }\n\n /**\n * Creates a MobileContactInfo instance from a PayoutDTO object.\n * Extracts mobile contact information from payout data structure.\n * The country code is derived from the parsed phone number.\n *\n * **Process:**\n * 1. Validates country code format in DTO\n * 2. Parses phone number from msisdn field (uses DTO country as hint)\n * 3. Uses phone number's country code (derived from E164 format)\n * 4. Handles MNO based on country MNP status:\n * - **MNP countries**: Uses channel field (required)\n * - **Non-MNP countries**: Auto-detects from phone number (ignores channel field)\n * 5. Uses payeeName as the contact name\n * 6. Delegates to constructor for validation\n *\n * @static\n * @param {PayoutDTO} info - The payout data transfer object\n * @param {string} info.msisdn - The phone number in E164 or local format\n * @param {string} info.countryCode - ISO2 country code (used as parsing hint)\n * @param {string} info.payeeName - The recipient's name\n * @param {string} [info.channel] - MNO ID (required for MNP countries, ignored for non-MNP countries)\n *\n * @returns {MobileContactInfo | undefined} New instance if successful, undefined if parsing fails\n *\n * @example\n * ```typescript\n * // Tanzania payout - channel ignored\n * const tzPayoutDTO = {\n * msisdn: \"+255712345678\", // Vodacom prefix\n * countryCode: \"TZ\",\n * payeeName: \"John Doe\",\n * channel: \"AIRTEL\" // Ignored, Vodacom auto-detected\n * };\n *\n * // Kenya payout - channel required\n * const kePayoutDTO = {\n * msisdn: \"+254712345678\",\n * countryCode: \"KE\",\n * payeeName: \"Jane Smith\",\n * channel: \"SAFARICOM\" // Required\n * };\n *\n * const tzContact = MobileContactInfo.fromPayoutDTO(tzPayoutDTO);\n * console.log(tzContact?.channelName); // \"M-Pesa\" (Vodacom's service)\n * console.log(tzContact?.countryCode); // \"TZ\" (from phoneNumber)\n * ```\n */\n public static fromPayoutDTO(info: PayoutDTO): MobileContactInfo | undefined {\n try {\n if (!CountryValidation.isISO2CountryCode(info.countryCode)) {\n console.error(`Invalid country code: ${info.countryCode}`);\n return undefined;\n }\n\n const phone = PhoneNumber.from(info.msisdn, { defaultCountry: info.countryCode });\n if (!phone) {\n console.error(`Failed to parse phone number: ${info.msisdn}`);\n return undefined;\n }\n\n let mnoId: MNOId | undefined;\n\n if (MNOUtils.requiresExplicitMNO(phone.countryCode)) {\n // MNP countries - use channel field (required)\n if (info.channel && typeof info.channel === \"string\") {\n if (MNOUtils.isValidMNOForCountry(info.channel, phone.countryCode)) {\n mnoId = info.channel as MNOId;\n } else {\n console.warn(`Invalid MNO ${info.channel} for country ${phone.countryCode} in PayoutDTO`);\n return undefined;\n }\n }\n // If no valid channel for MNP country, constructor will handle the error\n } else {\n // Non-MNP countries - always auto-detect (ignore channel field)\n mnoId = MNOUtils.getMNOByPhoneNumber(phone.e164Format, phone.countryCode)?.id;\n if (!mnoId) {\n console.warn(`Failed to auto-detect MNO for phone number ${phone.e164Format}`);\n return undefined;\n }\n }\n\n return new MobileContactInfo(info.payeeName, phone, mnoId);\n } catch (error) {\n console.error(\"Failed to create MobileContactInfo from PayoutDTO: \", info, error);\n return undefined;\n }\n }\n\n /**\n * Type guard to validate if an unknown object is a valid MobileContactInfo instance.\n * Performs comprehensive structural and data validation including country-specific MNO rules.\n *\n * **Validation Process:**\n * 1. **Structural validation**: Checks required properties exist with correct types\n * 2. **Name validation**: Ensures name is non-empty string\n * 3. **Phone validation**: Validates phone number can be parsed and is valid\n * 4. **MNO validation**: Country-specific validation:\n * - **MNP countries (KE)**: Validates MNO ID is valid for country\n * - **Non-MNP countries (TZ)**: Validates MNO matches phone number prefix (auto-detected)\n *\n * @static\n * @param {unknown} obj - The object to validate\n * @returns {obj is MobileContactInfo} Type predicate indicating validity\n *\n * @example\n * ```typescript\n * const unknownData = JSON.parse(someJsonString);\n *\n * if (MobileContactInfo.is(unknownData)) {\n * // TypeScript now knows this is MobileContactInfo\n * console.log(unknownData.name); // ✅ Type-safe access\n * console.log(unknownData.phoneNumber.e164Format); // ✅ Type-safe\n * console.log(unknownData.mnoId); // ✅ Type-safe\n * console.log(unknownData.countryCode); // ✅ Type-safe (from phoneNumber)\n * } else {\n * console.log(\"Invalid MobileContactInfo structure\");\n * }\n *\n * // Usage in arrays\n * const contacts = jsonArray.filter(MobileContactInfo.is);\n * // contacts is now typed as MobileContactInfo[]\n * ```\n *\n * @remarks\n * - Validates both serialized and live objects\n * - Phone numbers can be provided as strings or PhoneNumber objects\n * - Uses country-specific MNO validation through MNOUtils\n * - Returns false for any validation failure (fail-fast approach)\n * - For non-MNP countries, validates that the MNO matches what would be auto-detected\n */\n public static is(obj: unknown): obj is MobileContactInfo {\n if (!obj || typeof obj !== \"object\") return false;\n\n const mobileContactInfo = obj as Record<string, unknown>;\n\n // Check if name exists and is a string\n if (typeof mobileContactInfo.name !== \"string\" || !mobileContactInfo.name.trim()) {\n return false;\n }\n\n // Check if phoneNumber exists and is valid\n let phone_number: PhoneNumber | undefined = undefined;\n\n if (typeof mobileContactInfo.phoneNumber === \"string\") {\n phone_number = PhoneNumber.from(mobileContactInfo.phoneNumber);\n } else if (typeof mobileContactInfo.phoneNumber === \"object\") {\n const phoneObj = mobileContactInfo.phoneNumber;\n if (PhoneNumber.is(phoneObj)) {\n phone_number = PhoneNumber.from((phoneObj as any).e164Format);\n }\n }\n\n if (!phone_number) return false;\n\n // Check MNO ID validity\n const mnoId = mobileContactInfo.mnoId;\n if (typeof mnoId !== \"string\") return false;\n\n // Use MNOUtils for MNO validation\n if (!MNOUtils.isValidMNOForCountry(mnoId, phone_number.countryCode)) {\n return false;\n }\n\n // Use MNOUtils to check MNP requirements and consistency\n if (MNOUtils.requiresExplicitMNO(phone_number.countryCode)) {\n // For MNP countries, just verify the MNO is valid (we can't validate against phone number)\n return true;\n } else {\n // For non-MNP countries, verify MNO matches what would be auto-detected\n const derivedMNO = MNOUtils.getMNOByPhoneNumber(phone_number.e164Format, phone_number.countryCode);\n return derivedMNO?.id === mnoId;\n }\n }\n\n /**\n * Validates that all contact information is consistent and correct.\n * Uses country-specific validation logic through MNOUtils delegation.\n *\n * **Validation Checks:**\n * 1. **Name validation**: Non-empty string\n * 2. **Phone validation**: Valid phone number structure\n * 3. **MNO validation**: Valid MNO for the country\n * 4. **Consistency check**: For non-MNP countries, validates MNO matches auto-detected value from phone prefix\n *\n * @returns {boolean} True if all validations pass, false otherwise\n *\n * @example\n * ```typescript\n * const contact = new MobileContactInfo(\"John\", phoneNumber, TZMNOId.VODACOM);\n *\n * if (contact.validate()) {\n * console.log(\"Contact is valid and ready to use\");\n * } else {\n * console.log(\"Contact has validation issues\");\n * // Use getValidationDetails() for specific error information\n * }\n * ```\n *\n * @see {@link getValidationDetails} For detailed validation results with specific errors\n */\n public validate(): boolean {\n try {\n // Validate name\n if (!this.name?.trim()) return false;\n\n // Validate phone number\n if (!this.phoneNumber?.validate()) return false;\n\n // Use MNOUtils for MNO validation\n if (!MNOUtils.isValidMNOForCountry(this.mnoId, this.countryCode)) return false;\n\n // Use MNOUtils for phone number consistency check (non-MNP countries only)\n if (!MNOUtils.requiresExplicitMNO(this.countryCode)) {\n return MNOUtils.validateMNOForPhoneNumber(this.phoneNumber.e164Format, this.mnoId, this.countryCode);\n }\n\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Provides detailed validation results with specific error and warning information.\n * Uses country-specific validation logic for comprehensive analysis.\n *\n * **Validation Categories:**\n * - **Errors**: Critical issues that make the contact invalid\n * - **Warnings**: Potential issues that don't prevent usage but indicate inconsistencies\n *\n * @returns {Object} Detailed validation results\n * @returns {boolean} returns.isValid - True if no errors found\n * @returns {string[]} returns.errors - Array of error messages for critical issues\n * @returns {string[]} returns.warnings - Array of warning messages for potential issues\n *\n * @example\n * ```typescript\n * const contact = new MobileContactInfo(\"John\", phoneNumber);\n * const validation = contact.getValidationDetails();\n *\n * console.log(`Valid: ${validation.isValid}`);\n *\n * if (validation.errors.length > 0) {\n * console.log(\"Errors found:\");\n * validation.errors.forEach(error => console.log(`- ${error}`));\n * }\n *\n * if (validation.warnings.length > 0) {\n * console.log(\"Warnings:\");\n * validation.warnings.forEach(warning => console.log(`- ${warning}`));\n * }\n *\n * // For non-MNP countries, the MNO is always consistent as it's auto-detected\n * // Warnings would only appear if there were other potential issues\n * ```\n *\n * @remarks\n * Uses MNOUtils for country-specific validation logic:\n * - **Tanzania**: Validates MNO matches auto-detected value from phone number prefix\n * - **Kenya**: Acknowledges MNP limitations, focuses on MNO validity for country\n */\n public getValidationDetails(): {\n isValid: boolean;\n errors: string[];\n warnings: string[];\n } {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n if (!this.name?.trim()) {\n errors.push(\"Name is required\");\n }\n\n if (!this.phoneNumber?.validate()) {\n errors.push(\"Phone number is invalid\");\n }\n\n // Use MNOUtils for validation\n if (!MNOUtils.isValidMNOForCountry(this.mnoId, this.countryCode)) {\n errors.push(`Invalid MNO ${this.mnoId} for country ${this.countryCode}`);\n }\n\n // Check for potential MNP issues using MNOUtils\n if (!MNOUtils.requiresExplicitMNO(this.countryCode)) {\n const derivedMNO = MNOUtils.getMNOByPhoneNumber(this.phoneNumber.e164Format, this.countryCode);\n if (derivedMNO?.id !== this.mnoId) {\n errors.push(\"MNO doesn't match auto-detected value from phone number prefix\");\n }\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n warnings,\n };\n }\n\n /**\n * Gets the contact's personal name for display purposes.\n *\n * @override\n * @returns {string} The contact's personal name\n */\n override get accountName(): string {\n return this.name;\n }\n\n /**\n * Gets the phone number formatted for display and storage.\n * Returns the number in e164 format (with + prefix).\n *\n * @override\n * @returns {string} Phone number in e164 format (e.g., \"+255712345678\")\n *\n * @example\n * ```typescript\n * const contact = new MobileContactInfo(\"John\", phoneNumber);\n * console.log(contact.accountNumber); // \"+255712345678\"\n * ```\n */\n override get accountNumber(): string {\n return this.phoneNumber.getWithFormat(PhoneNumberFormat.E164);\n }\n\n /**\n * Gets the localized label for the display name field.\n *\n * @override\n * @returns {string} Always returns \"Name\" for mobile contacts\n */\n override get accountNameLabel(): string {\n return \"Name\";\n }\n\n /**\n * Gets the localized label for the account number field.\n *\n * @override\n * @returns {string} Always returns \"Phone Number\" for mobile contacts\n */\n override get accountNumberLabel(): string {\n return \"Phone Number\";\n }\n\n /**\n * Gets the localized label for the channel field.\n *\n * @override\n * @returns {string} Always returns \"Channel\" for mobile contacts\n */\n override get channelLabel(): string {\n return \"Channel\";\n }\n\n /**\n * Gets the typed MNO identifier for this mobile contact.\n * This is the programmatic identifier used for API calls and business logic.\n *\n * @returns {MNOId} The MNO identifier (e.g., \"VODACOM\", \"SAFARICOM\")\n *\n * @example\n * ```typescript\n * const contact = new MobileContactInfo(\"John\", tzPhone, TZMNOId.VODACOM);\n * console.log(contact.channelId); // \"VODACOM\" (for MNP countries)\n *\n * // For non-MNP countries, always returns auto-detected value\n * const contact2 = new MobileContactInfo(\"John\", tzPhone, TZMNOId.AIRTEL); // AIRTEL ignored\n * console.log(contact2.channelId); // \"VODACOM\" (auto-detected from phone prefix)\n * ```\n */\n get channelId(): MNOId {\n return this.mnoId;\n }\n\n /**\n * Gets the human-readable mobile money service name for display purposes.\n * Uses MNOUtils to retrieve service information from country-specific implementations.\n *\n * **Behavior:**\n * - Returns the mobile money service name (e.g., \"M-Pesa\", \"Airtel Money\")\n * - Falls back to MNO display name if service name unavailable\n * - Falls back to MNO ID if no display information available\n * - For non-MNP countries, always reflects the auto-detected MNO service\n *\n * @returns {string} The mobile money service name for display\n *\n * @example\n * ```typescript\n * const vodacomContact = new MobileContactInfo(\"John\", tzPhone, TZMNOId.VODACOM);\n * console.log(vodacomContact.channelName); // \"M-Pesa\"\n *\n * const safaricomContact = new MobileContactInfo(\"Jane\", kePhone, KEMNOId.SAFARICOM);\n * console.log(safaricomContact.channelName); // \"M-Pesa\"\n *\n * // For non-MNP countries, shows auto-detected service regardless of provided MNO\n * const tzContact = new MobileContactInfo(\"Bob\", tzVodacomPhone, TZMNOId.AIRTEL); // AIRTEL ignored\n * console.log(tzContact.channelName); // \"M-Pesa\" (Vodacom's service, auto-detected)\n * ```\n *\n * @remarks\n * Uses MNOUtils.getMNOById() which delegates to country-specific implementations\n * for consistent and accurate service name retrieval.\n */\n get channelName(): string {\n const mno = MNOUtils.getMNOById(this.mnoId, this.countryCode);\n return mno?.mobileMoneyService ?? mno?.displayName ?? this.mnoId;\n }\n}\n\n/**\n * Implementation of BaseContactInfo for bank account contacts.\n * Handles storage, validation, and display of contact details specific to bank transfers.\n * The country code is automatically derived from the Bank object.\n *\n * This class provides comprehensive validation for bank account information including\n * SWIFT code validation, account name verification, and account number format checking.\n *\n * **Key Features:**\n * - SWIFT code validation per country regulations\n * - Account name format validation\n * - Account number format validation (country-specific rules)\n * - Integration with Bank service for institution data\n * - Country code derived from Bank (single source of truth)\n *\n * @extends BaseContactInfo\n * @class BankContactInfo\n *\n * @property {string} accName - The bank account holder's name\n * @property {Bank} bank - The bank institution object with SWIFT code and details\n * @property {string} accNo - The bank account number\n *\n * @example\n * ```typescript\n * // Create bank contact with validation\n * const bank = Bank.from(\"CORUTZTZ\", \"TZ\"); // CRDB Bank\n * const contact = new BankContactInfo(\"John Doe\", bank, \"0150123456789\");\n *\n * console.log(contact.channelId); // \"CORUTZTZ\" (SWIFT code)\n * console.log(contact.channelName); // \"CRDB\" (bank short name)\n * console.log(contact.accountName); // \"John Doe\"\n * console.log(contact.accountNumber); // \"0150123456789\"\n * console.log(contact.countryCode); // \"TZ\" (from bank)\n * ```\n *\n * @throws {ContactInfoError} When validation fails (invalid account name, number, or bank)\n */\nexport class BankContactInfo extends BaseContactInfo {\n /**\n * Creates a new bank contact with comprehensive validation.\n * The country code is automatically extracted from the Bank object.\n *\n * **Validation Process:**\n * 1. **Country code extraction**: Derived from bank.countryCode\n * 2. **Account name validation**: Checks format and character requirements\n * 3. **Account number validation**: Validates format per country banking rules\n * 4. **Bank validation**: Ensures bank object is valid and contains required data\n *\n * @param {string} accName - The bank account holder's name (must pass BankValidation.validateAccountName)\n * @param {Bank} bank - The bank institution object (must be valid Bank instance with countryCode)\n * @param {string} accNo - The bank account number (must pass country-specific validation)\n *\n * @throws {ContactInfoError} When validation fails:\n * - Invalid account name format\n * - Invalid account number format for the country\n * - Invalid or missing bank information\n *\n * @example\n * ```typescript\n * try {\n * const bank = Bank.from(\"CORUTZTZ\", \"TZ\");\n * const contact = new BankContactInfo(\"John Doe\", bank, \"0150123456789\");\n * console.log(\"Bank contact created successfully\");\n * console.log(`Country: ${contact.countryCode}`); // \"TZ\" (from bank)\n * } catch (error) {\n * if (error instanceof ContactInfoError) {\n * console.log(`Validation failed: ${error.message}`);\n * }\n * }\n * ```\n */\n constructor(public readonly accName: string, public readonly bank: Bank, public readonly accNo: string) {\n super(ContactType.Bank, bank.countryCode);\n\n // Validate account name\n if (!BankValidation.validateAccountName(accName)) {\n throw new ContactInfoError(\"Invalid account name\", {\n operation: \"constructor\",\n countryCode: bank.countryCode,\n });\n }\n\n // Validate account number\n if (!BankValidation.validateAccountNumber(accNo, bank.countryCode)) {\n throw new ContactInfoError(\"Invalid account number\", {\n operation: \"constructor\",\n countryCode: bank.countryCode,\n });\n }\n }\n\n /**\n * Creates a BankContactInfo instance from a ContactDTO object.\n * Handles SWIFT code validation and bank lookup.\n * The country code is derived from the SWIFT code.\n *\n * **Process:**\n * 1. Validates DTO type is \"Bank\"\n * 2. Extracts SWIFT code from channel field\n * 3. Derives country code from SWIFT code\n * 4. Validates SWIFT code format and existence\n * 5. Looks up bank information using SWIFT code\n * 6. Delegates to constructor for final validation\n *\n * @static\n * @param {ContactDTO} info - The contact data transfer object\n * @param {string} info.type - Must be \"Bank\"\n * @param {string} info.channel - The SWIFT code for the bank\n * @param {string} info.displayName - The account holder's name\n * @param {string} info.accountNo - The bank account number\n *\n * @returns {BankContactInfo | undefined} New instance if successful, undefined if validation fails\n *\n * @example\n * ```typescript\n * const contactDTO = {\n * type: \"Bank\",\n * channel: \"CORUTZTZ\", // CRDB Bank SWIFT code\n * displayName: \"John Doe\",\n * accountNo: \"0150123456789\"\n * };\n *\n * const contact = BankContactInfo.fromContactDTO(contactDTO);\n * if (contact) {\n * console.log(`Created bank contact: ${contact.accountName} at ${contact.bank.shortName}`);\n * console.log(`Country: ${contact.countryCode}`); // \"TZ\" (derived from SWIFT)\n * }\n * ```\n */\n public static fromContactDTO(info: ContactDTO): BankContactInfo | undefined {\n if (info.type !== \"Bank\") return undefined;\n\n const swiftCode = info.channel;\n\n if (!swiftCode || typeof swiftCode !== \"string\") {\n console.error(\"SWIFT code is required for bank contacts\");\n return undefined;\n }\n\n const countryCode = BankValidation.getCountryFromSwiftCode(swiftCode);\n if (!countryCode) {\n console.error(`Could not identify country from SWIFT code: ${swiftCode}`);\n return undefined;\n }\n\n if (!BankValidation.validateSwiftCode(swiftCode, countryCode)) {\n console.error(`Invalid SWIFT code ${swiftCode} for country ${countryCode}`);\n return undefined;\n }\n\n try {\n const bank = Bank.from(swiftCode, countryCode);\n if (!bank) {\n throw new Error(`Bank with SWIFT code ${swiftCode} not found`);\n }\n return new BankContactInfo(info.displayName, bank, info.accountNo);\n } catch (error) {\n console.error(`Failed to create BankContactInfo: ${error}`);\n return undefined;\n }\n }\n\n /**\n * Creates a BankContactInfo instance from a PayoutDTO object.\n * Parses bank information from the payout's msisdn field using \"swiftcode:accountno\" format.\n * The country code is derived from the parsed phone number or validated against the SWIFT code.\n *\n * **Expected Format:** The msisdn field should contain \"SWIFTCODE:ACCOUNTNUMBER\"\n *\n * **Process:**\n * 1. Validates country code format in DTO (used as hint/validation)\n * 2. Splits msisdn field on \":\" delimiter\n * 3. Validates SWIFT code format and existence\n * 4. Looks up bank information (bank contains country code)\n * 5. Creates contact with parsed information\n *\n * @static\n * @param {PayoutDTO} info - The payout data transfer object\n * @param {string} info.msisdn - Bank info in format \"SWIFTCODE:ACCOUNTNUMBER\"\n * @param {string} info.countryCode - ISO2 country code (used for validation)\n * @param {string} info.payeeName - The account holder's name\n *\n * @returns {BankContactInfo | undefined} New instance if successful, undefined if parsing fails\n *\n * @example\n * ```typescript\n * const payoutDTO = {\n * msisdn: \"CORUTZTZ:0150123456789\", // SWIFT:Account format\n * countryCode: \"TZ\",\n * payeeName: \"Jane Smith\"\n * };\n *\n * const contact = BankContactInfo.fromPayoutDTO(payoutDTO);\n * if (contact) {\n * console.log(`Payout to: ${contact.accountName}`);\n * console.log(`Bank: ${contact.bank.fullName}`);\n * console.log(`Account: ${contact.accountNumber}`);\n * console.log(`Country: ${contact.countryCode}`); // \"TZ\" (from bank)\n * }\n * ```\n *\n * @remarks\n * Returns undefined if:\n * - msisdn doesn't contain exactly one \":\" separator\n * - SWIFT code is invalid for the country\n * - Bank lookup fails\n * - Any validation error occurs\n */\n public static fromPayoutDTO(info: PayoutDTO): BankContactInfo | undefined {\n try {\n if (!CountryValidation.isISO2CountryCode(info.countryCode)) {\n console.error(`Invalid country code: ${info.countryCode}`);\n return undefined;\n }\n\n const text = info.msisdn.trim().split(\":\");\n if (text.length !== 2) {\n console.error(\"Invalid PayoutDTO format for bank - expected 'swiftcode:accountno'\");\n return undefined;\n }\n\n const [swiftcode, accNo] = text;\n\n if (!BankValidation.validateSwiftCode(swiftcode, info.countryCode)) {\n console.error(`Invalid SWIFT code ${swiftcode} for country ${info.countryCode}`);\n return undefined;\n }\n\n const bank = Bank.from(swiftcode, info.countryCode);\n if (!bank) {\n console.error(`Bank with SWIFT code ${swiftcode} not found`);\n return undefined;\n }\n\n return new BankContactInfo(info.payeeName, bank, accNo);\n } catch (error) {\n console.error(\"Failed to create BankContactInfo from PayoutDTO: \", info, error);\n return undefined;\n }\n }\n\n /**\n * Type guard to validate if an unknown object is a valid BankContactInfo instance.\n * Performs comprehensive structural and business logic validation.\n *\n * **Validation Process:**\n * 1. **Structural validation**: Checks required properties exist with correct types\n * 2. **Account name validation**: Uses BankValidation.validateAccountName()\n * 3. **Account number validation**: Uses country-specific BankValidation.validateAccountNumber()\n * 4. **Bank validation**: Ensures bank object passes Bank.is() validation\n *\n * @static\n * @param {unknown} obj - The object to validate\n * @returns {obj is BankContactInfo} Type predicate indicating validity\n *\n * @example\n * ```typescript\n * const unknownData = JSON.parse(bankJsonString);\n *\n * if (BankContactInfo.is(unknownData)) {\n * // TypeScript now knows this is BankContactInfo\n * console.log(unknownData.accName); // ✅ Type-safe access\n * console.log(unknownData.bank.fullName); // ✅ Type-safe\n * console.log(unknownData.accNo); // ✅ Type-safe\n * console.log(unknownData.countryCode); // ✅ Type-safe (from bank)\n * } else {\n * console.log(\"Invalid BankContactInfo structure\");\n * }\n *\n * // Usage in arrays\n * const bankContacts = jsonArray.filter(BankContactInfo.is);\n * // bankContacts is now typed as BankContactInfo[]\n * ```\n *\n * @remarks\n * - Validates both serialized and live objects\n * - Uses banking industry validation rules through BankValidation\n * - Ensures bank object contains valid SWIFT code and institution data\n * - Returns false for any validation failure (fail-fast approach)\n */\n public static is(obj: unknown): obj is BankContactInfo {\n if (!obj || typeof obj !== \"object\") return false;\n\n const bankContactInfo = obj as Partial<BankContactInfo>;\n\n if (typeof bankContactInfo.accName !== \"string\") return false;\n if (typeof bankContactInfo.accNo !== \"string\") return false;\n if (!bankContactInfo.bank || !Bank.is(bankContactInfo.bank)) return false;\n\n const accName = bankContactInfo.accName;\n const accNumber = bankContactInfo.accNo;\n const bank = bankContactInfo.bank;\n\n const validAccName = BankValidation.validateAccountName(accName);\n const validAccNumber = BankValidation.validateAccountNumber(accNumber, bank.countryCode);\n\n return validAccName && validAccNumber;\n }\n\n public validate(): boolean {\n try {\n return (\n BankValidation.validateAccountName(this.accName) &&\n BankValidation.validateAccountNumber(this.accNo, this.bank.countryCode) &&\n Bank.is(this.bank)\n );\n } catch {\n return false;\n }\n }\n\n /**\n * Gets the bank account holder's name for display purposes.\n *\n * @override\n * @returns {string} The account holder's name\n */\n override get accountName(): string {\n return this.accName;\n }\n\n /**\n * Gets the bank account number for display and processing.\n *\n * @override\n * @returns {string} The bank account number as provided\n */\n override get accountNumber(): string {\n return this.accNo;\n }\n\n /**\n * Gets the localized label for the display name field.\n *\n * @override\n * @returns {string} Always returns \"Acc. Name\" for bank contacts\n */\n override get accountNameLabel(): string {\n return \"Acc. Name\";\n }\n\n /**\n * Gets the localized label for the account number field.\n *\n * @override\n * @returns {string} Always returns \"Bank Acc. No.\" for bank contacts\n */\n override get accountNumberLabel(): string {\n return \"Bank Acc. No.\";\n }\n\n /**\n * Gets the localized label for the channel field.\n *\n * @override\n * @returns {string} Always returns \"Bank\" for bank contacts\n */\n override get channelLabel(): string {\n return \"Bank\";\n }\n\n /**\n * Gets the bank's SWIFT code as the channel identifier.\n * This is the programmatic identifier used for bank transfers and API calls.\n *\n * @returns {BankSwiftCode} The bank's SWIFT/BIC code (e.g., \"CORUTZTZ\", \"KCBLKENX\")\n *\n * @example\n * ```typescript\n * const contact = new BankContactInfo(\"John\", crdBank, \"123456789\");\n * console.log(contact.channelId); // \"CORUTZTZ\"\n *\n * // Type-safe usage\n * if (contact.channelId === \"CORUTZTZ\") {\n * console.log(\"This is a CRDB Bank account\");\n * }\n * ```\n */\n get channelId(): BankSwiftCode {\n return this.bank.swiftCode;\n }\n\n /**\n * Gets the bank's short name for display purposes.\n * Provides a human-readable identifier for the banking institution.\n *\n * @returns {string} The bank's abbreviated name (e.g., \"CRDB\", \"KCB\", \"Equity\")\n *\n * @example\n * ```typescript\n * const crdContact = new BankContactInfo(\"John\", crdBank, \"123456789\");\n * console.log(crdContact.channelName); // \"CRDB\"\n *\n * const kcbContact = new BankContactInfo(\"Jane\", kcbBank, \"987654321\");\n * console.log(kcbContact.channelName); // \"KCB\"\n * ```\n *\n * @remarks\n * The short name is maintained by the Bank service and represents\n * the commonly used abbreviation for the banking institution.\n */\n get channelName(): string {\n return this.bank.shortName;\n }\n}\n\n/**\n * Union type representing either a mobile or bank contact.\n * Used for type-safe handling of contact information throughout the application.\n *\n * This discriminated union allows for:\n * - **Type-safe polymorphism**: Handle different contact types with single interface\n * - **Runtime type discrimination**: Use `contact.type` to determine specific type\n * - **Comprehensive validation**: Each type provides its own validation logic\n * - **Consistent API**: Both types implement BaseContactInfo interface\n * - **Country code access**: Always available via contact.countryCode (derived from domain objects)\n *\n * @typedef {MobileContactInfo | BankContactInfo} ContactInfo\n *\n * @example\n * ```typescript\n * // Type-safe handling of mixed contact types\n * function processContact(contact: ContactInfo) {\n * console.log(`Processing ${contact.type} contact: ${contact.accountName}`);\n * console.log(`Channel: ${contact.channelName}`);\n * console.log(`Country: ${contact.countryCode}`); // Always available\n *\n * // Type discrimination for specific behavior\n * if (contact.type === \"Mobile\") {\n * // TypeScript knows this is MobileContactInfo\n * console.log(`MNO: ${contact.mnoId}`);\n * console.log(`Phone: ${contact.phoneNumber.e164Format}`);\n * } else {\n * // TypeScript knows this is BankContactInfo\n * console.log(`Bank: ${contact.bank.fullName}`);\n * console.log(`SWIFT: ${contact.bank.swiftCode}`);\n * }\n * }\n *\n * // Usage with arrays\n * const contacts: ContactInfo[] = [\n * new MobileContactInfo(\"John\", phoneNumber, TZMNOId.VODACOM),\n * new BankContactInfo(\"Jane\", bank, \"123456789\")\n * ];\n *\n * contacts.forEach(processContact);\n *\n * // Type guards work seamlessly\n * const mobileContacts = contacts.filter((c): c is MobileContactInfo => c.type === \"Mobile\");\n * const bankContacts = contacts.filter((c): c is BankContactInfo => c.type === \"Bank\");\n * ```\n *\n * @see {@link MobileContactInfo} For mobile money contact implementation\n * @see {@link BankContactInfo} For bank transfer contact implementation\n * @see {@link BaseContactInfo} For common interface and behavior\n */\nexport type ContactInfo = MobileContactInfo | BankContactInfo;","import { type ContactDTO, ContactDTOSchemas, ContactType } from \"../features/contact/contact.dtos\";\nimport { BankContactInfo, type ContactInfo, MobileContactInfo } from \"./contact-info.model\";\n\n/**\n * Contact class that wraps the Zod schema and provides additional functionality\n */\nexport class Contact {\n private readonly data: ContactDTO;\n\n /**\n * Private constructor - use static methods to create instances\n */\n private constructor(data: ContactDTO) {\n this.data = ContactDTOSchemas.contactDTO.parse(data);\n }\n\n /**\n * Unique identifier for the contact\n */\n get id(): string {\n return this.data.id;\n }\n\n /**\n * Profile identifier associated with this contact\n */\n get profileId(): string {\n return this.data.profileId;\n }\n\n /**\n * Display name of the contact\n */\n get displayName(): string {\n return this.data.displayName;\n }\n\n /**\n * Type of contact (Bank or Mobile)\n */\n get type(): ContactType {\n return this.data.type;\n }\n\n /**\n * Creation timestamp of the contact\n */\n get createdAt(): Date {\n return new Date(this.data.createdAt);\n }\n\n /**\n * Update timestamp of the contact\n */\n get updatedAt(): Date {\n return new Date(this.data.updatedAt);\n }\n\n /**\n * Detailed contact information based on contact type\n *\n * @returns {ContactInfo | undefined} Contact information object:\n * - MobileContactInfo for mobile money contacts\n * - BankContactInfo for bank contacts\n * - undefined if contact information cannot be constructed\n *\n * @remarks\n * For mobile contacts, constructs from phone number\n * For bank contacts, constructs from SWIFT code and account number\n */\n get info(): ContactInfo | undefined {\n if (this.data.type === ContactType.Mobile) {\n return MobileContactInfo.fromContactDTO(this.data);\n }\n\n if (this.data.type === ContactType.Bank) {\n return BankContactInfo.fromContactDTO(this.data);\n }\n\n return undefined;\n }\n\n /**\n * Account number for the contact\n *\n * @returns {string} Account number:\n * - For valid contacts, returns formatted account number from ContactInfo\n * - For invalid contacts, falls back to raw account number\n */\n get accNo(): string {\n const info = this.info;\n if (info) return info.accountNumber;\n return this.data.accountNo;\n }\n\n /**\n * Account name for the contact\n * Always returns the display name\n */\n get accName(): string {\n return this.data.displayName;\n }\n\n /**\n * Label for the account number field based on contact type\n *\n * @returns {string} Appropriate label:\n * - \"Phone Number\" for mobile contacts\n * - \"Bank Account Number\" for bank contacts\n * - \"Account Number\" as fallback\n */\n get accNoLabel(): string {\n const info = this.info;\n if (info instanceof MobileContactInfo) return \"Phone Number\";\n if (info instanceof BankContactInfo) return \"Bank Account Number\";\n return \"Account Number\";\n }\n\n /**\n * Label for the channel field based on contact type\n *\n * @returns {string} Appropriate label:\n * - \"Channel\" for mobile contacts\n * - \"Bank\" for bank contacts\n * - \"Channel\" as fallback\n */\n get channelLabel(): string {\n const info = this.info;\n if (info instanceof MobileContactInfo) return \"Channel\";\n if (info instanceof BankContactInfo) return \"Bank\";\n return \"Channel\";\n }\n\n /**\n * Label for the account name field based on contact type\n *\n * @returns {string} Appropriate label:\n * - \"Full Name\" for mobile contacts\n * - \"Bank Account Name\" for bank contacts\n * - \"Display Name\" as fallback\n */\n get accNameLabel(): string {\n const info = this.info;\n if (info instanceof MobileContactInfo) return \"Full Name\";\n if (info instanceof BankContactInfo) return \"Bank Account Name\";\n return \"Display Name\";\n }\n\n get channelName(): string {\n const info = this.info;\n if (info instanceof MobileContactInfo) return info.channelName;\n if (info instanceof BankContactInfo) return info.channelName;\n return \"\";\n }\n\n /**\n * Creates a Contact instance from raw data\n * @throws {ZodError} if validation fails\n */\n public static create(data: ContactDTO): Contact {\n return new Contact(data);\n }\n\n /**\n * Creates multiple Contact instances from an array of raw data\n * @throws {ZodError} if validation fails for any item\n */\n public static createMany(dataArray: ContactDTO[]): Contact[] {\n return dataArray.map((data) => new Contact(data));\n }\n\n /**\n * Creates a Contact instance from raw data without throwing\n * @returns {Contact | null} Contact instance or null if validation fails\n */\n public static createSafe(data: ContactDTO): Contact | null {\n try {\n return new Contact(data);\n } catch {\n return null;\n }\n }\n\n /**\n * Checks if an unknown value contains valid data to construct a Contact instance.\n * This is useful when validating raw data structures before instantiation.\n *\n * @param {unknown} obj - The value containing potential contact data\n * @returns {obj is Contact} Type predicate indicating if a Contact can be constructed\n *\n * @example\n * ```typescript\n * const rawData = await fetchFromAPI();\n * if (Contact.canConstruct(rawData)) {\n * const contact = Contact.create(rawData);\n * // TypeScript knows contact is valid here\n * console.log(contact.displayName);\n * }\n * ```\n *\n * @throws {never} This method never throws errors\n *\n * @remarks\n * This method performs strict validation against the {@link ContactDTO} schema.\n */\n public static canConstruct(obj: unknown): obj is Contact {\n if (!obj || typeof obj !== \"object\") return false;\n\n const result = ContactDTOSchemas.contactDTO.safeParse(obj);\n if (!result.success) return false;\n\n const contact = Contact.createSafe(result.data);\n return contact !== null;\n }\n\n /**\n * Validates if an unknown value is a Contact instance.\n * This is a runtime type guard that ensures proper object structure and data validity.\n *\n * @param {unknown} obj - The value to validate\n * @returns {obj is Contact} Type predicate indicating if the value is a valid Contact\n *\n * @example\n * ```typescript\n * const maybeContact = getContactFromCache();\n * if (Contact.is(maybeContact)) {\n * // TypeScript knows maybeContact is a Contact here\n * console.log(maybeContact.displayName);\n * }\n * ```\n *\n * @throws {never} This method never throws errors\n *\n * @remarks\n * This method performs a complete structural validation:\n * 1. Checks if the value is an object\n * 2. Verifies presence of internal data property\n * 3. Validates the data against ContactData schema\n * 4. Ensures the object is a proper Contact instance\n *\n * Use this method when:\n * - Validating cached Contact instances\n * - Checking serialized Contact objects\n * - Verifying API responses\n * - Type narrowing in conditional blocks\n */\n public static is(obj: unknown): obj is Contact {\n if (!obj || typeof obj !== \"object\") return false;\n if (!(\"data\" in obj)) return false;\n\n return Contact.canConstruct(obj.data);\n }\n\n /**\n * Converts Payout instance to a plain object\n */\n toJSON(): ContactDTO {\n return { ...this.data };\n }\n}\n","import { CountryValidation, CurrencyValidation } from \"@temboplus/frontend-core\";\nimport { z } from \"zod\";\n\n/**\n * Represents the available channels through which payouts can be processed.\n * @enum {string}\n * @readonly\n */\nexport enum PayoutChannel {\n /** Payment processed through mobile money services */\n MOBILE = \"Mobile\",\n /** Payment processed through traditional banking channels */\n BANK = \"Bank\",\n}\n\n/**\n * Represents the current status of a payout transaction.\n * @enum {string}\n * @readonly\n */\nexport enum PayoutStatus {\n /** Payout has been initially created but not yet processed */\n CREATED = \"CREATED\",\n /** Payout is awaiting processing */\n PENDING = \"PENDING\",\n /** Payout has been successfully processed */\n PAID = \"PAID\",\n /** Payout processing has failed */\n FAILED = \"FAILED\",\n /** Payout has been rejected */\n REJECTED = \"REJECTED\",\n /** Payout has been reversed */\n REVERSED = \"REVERSED\",\n}\n\n/**\n * Represents the approval status for payouts that require authorization.\n * @enum {string}\n * @readonly\n */\nexport enum PayoutApprovalStatus {\n /** Payout is awaiting approval decision */\n PENDING = \"Pending\",\n /** Payout has been approved */\n APPROVED = \"Approved\",\n /** Payout has been rejected during approval process */\n REJECTED = \"Rejected\",\n}\n\n/**\n * Schema for identifying users in the payout process\n * Used to track who created or actioned a payout\n */\nconst AuthorizerSchema = z.object({\n id: z.string(),\n name: z.string(),\n identity: z.string(),\n});\n\n/**\n * Schema for payout status\n * Defines all possible states a payout can be in\n */\nconst PayoutStatusSchema = z.nativeEnum(PayoutStatus);\n\n/**\n * Schema for payout approval status\n * Defines all possible approval states\n */\nconst PayoutApprovalStatusSchema = z.nativeEnum(PayoutApprovalStatus);\n\n/**\n * Base schema for payout operations\n * Contains common fields used across all payout operations\n */\nconst BasePayoutSchema = z.object({\n channel: z.string(),\n msisdn: z.string(),\n amount: z.coerce.number(),\n description: z.string(),\n notes: z.string().nullish(),\n});\n\n/**\n * Schema for creating a new payout\n * Extends base payout schema with payee information\n */\nconst PayoutInputDTOSchema = BasePayoutSchema.extend({\n payeeName: z.string(),\n});\n\n/**\n * Schema for complete payout record\n * Extends base payout schema with additional fields for:\n * - Identification (id, profileId)\n * - Status tracking\n * - Timestamps\n * - Approval information\n * - User tracking (created by, actioned by)\n */\nconst PayoutDTOSchema = BasePayoutSchema.extend({\n id: z.string(),\n profileId: z.string(),\n payeeName: z.string(),\n // Validate countryCode as a string present in the imported Set\n countryCode: z\n .string()\n .default(\"TZ\")\n .refine((code) => CountryValidation.isISO2CountryCode(code), {\n message: \"Provided country code is not a valid ISO2 code.\",\n }),\n // Validate currencyCode as a string present in the imported Set\n currencyCode: z\n .string()\n .default(\"TZS\")\n .refine((code) => CurrencyValidation.isCurrencyCode(code), {\n message: \"Provided currency code is not a valid currency code.\",\n }),\n channel: z.string(),\n status: PayoutStatusSchema,\n statusMessage: z.string(),\n partnerReference: z.string().nullish(),\n createdAt: z.coerce.date(),\n updatedAt: z.coerce.date(),\n actionedAt: z.coerce.date().nullish(),\n approvalStatus: PayoutApprovalStatusSchema.nullish(),\n createdBy: AuthorizerSchema.nullish(),\n actionedBy: AuthorizerSchema.nullish(),\n});\n\n/**\n * Type definition inferred from AuthorizerSchema\n * Represents the structure of a user who actions the payout\n */\ntype PayoutAuthorizerDTO = z.infer<typeof AuthorizerSchema>;\n\n/**\n * Type definition inferred from PayoutInputDTOSchema\n * Represents the structure of data required to create a new payout\n */\ntype PayoutInputDTO = z.infer<typeof PayoutInputDTOSchema>;\n\n/**\n * Type definition inferred from PayoutDTOSchema\n * Represents the complete payout record structure\n */\ntype PayoutDTO = z.infer<typeof PayoutDTOSchema>;\n\n/**\n * Export object containing all payout-related schemas\n * Used for validation and type checking throughout the application\n */\nexport const PayoutDTOSchemas = {\n PayoutDTO: PayoutDTOSchema,\n PayoutInputDTO: PayoutInputDTOSchema,\n PayoutStatus: PayoutStatusSchema,\n PayoutApprovalStatus: PayoutApprovalStatusSchema,\n PayoutAuthorizer: AuthorizerSchema,\n} as const;\n\n/**\n * Export types for use in other parts of the application\n * These types can be used for type checking and documentation\n */\nexport type { PayoutAuthorizerDTO as PayoutAuthorizer, PayoutDTO, PayoutInputDTO };\n","import { Amount, CurrencyCode } from \"@temboplus/frontend-core\";\nimport {\n PayoutApprovalStatus,\n PayoutAuthorizer,\n PayoutDTO,\n PayoutDTOSchemas,\n PayoutStatus,\n} from \"../features/payout/payout.dtos\";\nimport { ContactInfo, MobileContactInfo, BankContactInfo } from \"./contact-info.model\";\n\n/**\n * Payout class that wraps the Zod schema and provides additional functionality\n */\nexport class Payout {\n private readonly data: PayoutDTO;\n\n /**\n * Private constructor - use static methods to create instances\n */\n private constructor(data: PayoutDTO) {\n this.data = PayoutDTOSchemas.PayoutDTO.parse(data);\n }\n\n // Getters for all properties\n /** Unique identifier for the payout */\n get id(): string {\n return this.data.id;\n }\n\n /** Profile identifier associated with this payout */\n get profileId(): string {\n return this.data.profileId;\n }\n\n /** Name of the payee/recipient */\n get payeeName(): string {\n return this.data.payeeName;\n }\n\n /** Payment channel used for this payout */\n get channel(): string {\n return this.data.channel;\n }\n\n /** Mobile number or bank account identifier */\n get msisdn(): string {\n return this.data.msisdn;\n }\n\n /**\n * Amount to be paid out\n * @returns {Amount} Amount object representing the payout value\n */\n get amount(): Amount {\n return Amount.from(this.data.amount, this.data.currencyCode as CurrencyCode)!;\n }\n\n /** Description of the payout purpose */\n get description(): string {\n return this.data.description;\n }\n\n /** Optional additional notes about the payout */\n get notes(): string | undefined | null {\n return this.data.notes;\n }\n\n /**\n * Current status of the payout\n * Derived from both approval status and transaction status:\n * - Returns REJECTED if approval status is \"Rejected\"\n * - Returns FAILED if approved but transaction failed\n * - Returns PAID if approved and transaction succeeded\n * - Returns PENDING if awaiting approval\n * - Falls back to transaction status in other cases\n *\n * @returns {PAYOUT_STATUS} Current status of the payout\n * @see {@link PAYOUT_STATUS} for all possible status values\n */\n get status(): PayoutStatus {\n if (this.data.approvalStatus === \"Rejected\") {\n return PayoutStatus.REJECTED;\n }\n if (this.data.approvalStatus === \"Approved\") {\n if (this.data.status === \"FAILED\") {\n return PayoutStatus.FAILED;\n }\n return PayoutStatus.PAID;\n }\n if (this.data.approvalStatus === \"Pending\") {\n return PayoutStatus.PENDING;\n }\n\n return this.data.status;\n }\n\n /** Status message providing details about current state */\n get statusMessage(): string {\n return this.data.statusMessage;\n }\n\n /** Optional reference ID from payment partner */\n get partnerReference(): string | undefined | null {\n return this.data.partnerReference;\n }\n\n /** Timestamp when payout was created */\n get createdAt(): Date {\n return this.data.createdAt;\n }\n\n /** Timestamp when payout was last updated */\n get updatedAt(): Date {\n return this.data.updatedAt;\n }\n\n /** Timestamp when payout was last updated */\n get actionedAt(): Date | undefined | null {\n return this.data.actionedAt;\n }\n\n /** Current approval status of the payout */\n get approvalStatus(): PayoutApprovalStatus | undefined | null {\n return this.data.approvalStatus;\n }\n\n /** Information about who created the payout */\n get createdBy(): PayoutAuthorizer | undefined | null {\n return this.data.createdBy;\n }\n\n /** Information about who last actioned the payout */\n get actionedBy(): PayoutAuthorizer | undefined | null {\n return this.data.actionedBy;\n }\n\n /**\n * Tries to construct contact information from the payout data.\n */\n get contactInfo(): ContactInfo | undefined {\n if (this.channel === \"TZ-BANK-B2C\") {\n return BankContactInfo.fromPayoutDTO(this.data);\n }\n return MobileContactInfo.fromPayoutDTO(this.data);\n }\n\n /**\n * Creates a Payout instance from raw data\n * @throws {ZodError} if validation fails\n */\n static create(data: PayoutDTO): Payout {\n return new Payout(data);\n }\n\n /**\n * Creates multiple Payout instances from an array of raw data\n * @throws {ZodError} if validation fails for any item\n */\n static createMany(dataArray: PayoutDTO[]): Payout[] {\n return dataArray.map((data) => new Payout(data));\n }\n\n /**\n * Creates a Payout instance from raw data without throwing\n * @returns {Payout | null} Payout instance or null if validation fails\n */\n public static createSafe(data: PayoutDTO): Payout | null {\n try {\n return new Payout(data);\n } catch {\n return null;\n }\n }\n\n /**\n * Checks if an unknown value contains valid data to construct a Payout instance.\n * This is useful when validating raw data structures before instantiation.\n *\n * @param {unknown} obj - The value containing potential payout data\n * @returns {obj is Payout} Type predicate indicating if a Payout can be constructed\n *\n * @example\n * ```typescript\n * const rawData = await fetchPayoutData();\n * if (Payout.canConstruct(rawData)) {\n * const payout = Payout.create(rawData);\n * // TypeScript knows payout is valid here\n * console.log(payout.amount.toString());\n * }\n * ```\n *\n * @throws {never} This method never throws errors\n *\n * @remarks\n * This method performs strict validation against the {@link PayoutData} schema\n */\n public static canConstruct(obj: unknown): obj is Payout {\n if (!obj || typeof obj !== \"object\") return false;\n\n const result = PayoutDTOSchemas.PayoutDTO.safeParse(obj);\n if (!result.success) return false;\n\n const payout = Payout.createSafe(result.data);\n return payout !== null;\n }\n\n /**\n * Validates if an unknown value is a Payout instance.\n * This is a runtime type guard that ensures proper object structure and data validity.\n *\n * @param {unknown} obj - The value to validate\n * @returns {obj is Payout} Type predicate indicating if the value is a valid Payout\n *\n * @example\n * ```typescript\n * const maybePayout = getPayoutFromCache();\n * if (Payout.is(maybePayout)) {\n * // TypeScript knows maybePayout is a Payout here\n * console.log(maybePayout.status);\n * }\n * ```\n *\n * @throws {never} This method never throws errors\n *\n * @remarks\n * This method performs a complete structural validation:\n * 1. Checks if the value is an object\n * 2. Verifies presence of internal data property\n * 3. Validates the data against PayoutData schema\n * 4. Ensures the object is a proper Payout instance\n *\n * Use this method when:\n * - Validating cached Payout instances\n * - Checking serialized Payout objects\n * - Verifying API responses\n * - Type narrowing in conditional blocks\n */\n public static is(obj: unknown): obj is Payout {\n if (!obj || typeof obj !== \"object\") return false;\n if (!(\"data\" in obj)) return false;\n\n return Payout.canConstruct(obj.data);\n }\n\n /**\n * Converts Payout instance to a plain object\n */\n public toJSON(): PayoutDTO {\n return { ...this.data };\n }\n}\n","import { z } from \"zod\";\n\n/**\n * Zod schema for validating user profile data.\n * Defines validation rules and constraints for each profile field.\n *\n * @const {ProfileType}\n *\n * @property {string} id - Unique identifier for the profile\n * @property {string | null | undefined} firstName - User's first name, can be null or undefined\n * @property {string | null | undefined} lastName - User's last name, can be null or undefined\n * @property {string} displayName - User's display name\n * @property {string | null | undefined} phone - User's contact phone number, can be null or undefined\n * @property {string} accountNo - User's account number\n * @property {string | null | undefined} email - User's email address, can be null or undefined\n * @property {boolean | null | undefined} autoApprove - Auto-approval setting, can be null or undefined\n */\nconst profileDTOSchema = z.object({\n id: z.string(),\n firstName: z.string().nullish(),\n lastName: z.string().nullish(),\n displayName: z.string(),\n phone: z.string().nullish(),\n accountNo: z.string().min(1),\n email: z.string().email().nullish(),\n autoApprove: z.boolean().nullish(),\n});\n\n/**\n * TypeScript type representing a validated user profile.\n * Use this type for profile instances that have been validated against the schema.\n */\nexport type ProfileDTO = z.infer<typeof profileDTOSchema>;\n\nexport const ProfileDTOSchemas = {\n profileDTOSchema,\n};\n","import { ProfileDTO, ProfileDTOSchemas } from \"@/features/auth/profile/profile.dtos\";\n\n/**\n * Represents a user profile in the system.\n *\n * This class provides methods for creating, validating, and manipulating user profile data.\n * It integrates with the Zod schema validation for data integrity.\n */\nexport class Profile {\n /** Unique identifier for the profile */\n private _id: string;\n /** User's first name */\n private _firstName?: string | null;\n /** User's last name */\n private _lastName?: string | null;\n /** User's display name, can be used for presentation */\n private _displayName: string;\n /** User's phone number */\n private _phone?: string | null;\n /** User's account number */\n private _accountNo: string;\n /** User's email address */\n private _email?: string | null;\n /** Auto-approval setting */\n private _autoApprove?: boolean | null;\n\n /**\n * Gets the profile schema used for validation.\n */\n static get schema() {\n return ProfileDTOSchemas.profileDTOSchema;\n }\n\n /**\n * Creates a new Profile instance with the provided data.\n *\n * Private constructor to enforce use of static factory methods.\n *\n * @param data - Object containing profile information\n */\n private constructor(data: {\n id: string;\n firstName?: string | null;\n lastName?: string | null;\n displayName: string;\n phone?: string | null;\n accountNo: string;\n email?: string | null;\n autoApprove?: boolean | null;\n }) {\n this._id = data.id;\n this._firstName = data.firstName;\n this._lastName = data.lastName;\n this._displayName = data.displayName;\n this._phone = data.phone;\n this._accountNo = data.accountNo;\n this._email = data.email;\n this._autoApprove = data.autoApprove;\n }\n\n /**\n * Creates a new Profile instance with the provided data.\n *\n * @param data - Object containing profile information.\n */\n static create(data: {\n id: string;\n firstName?: string | null;\n lastName?: string | null;\n displayName: string;\n phone?: string | null;\n accountNo: string;\n email?: string | null;\n autoApprove?: boolean | null;\n }): Profile | undefined {\n return new Profile({\n id: data.id,\n firstName: data.firstName,\n lastName: data.lastName,\n displayName: data.displayName,\n phone: data.phone,\n accountNo: data.accountNo,\n email: data.email,\n autoApprove: data.autoApprove,\n });\n }\n\n /**\n * Gets the profile's unique identifier.\n */\n get id(): string {\n return this._id;\n }\n\n /**\n * Gets the user's first name.\n */\n get firstName(): string | null | undefined {\n return this._firstName;\n }\n\n /**\n * Gets the user's last name.\n */\n get lastName(): string | null | undefined {\n return this._lastName;\n }\n\n /**\n * Gets the user's display name.\n */\n get displayName(): string {\n return this._displayName;\n }\n\n /**\n * Gets the user's phone number object.\n */\n get phone(): string | null | undefined {\n return this._phone;\n }\n\n /**\n * Gets the user's account number.\n */\n get accountNo(): string {\n return this._accountNo;\n }\n\n /**\n * Gets the user's email address.\n */\n get email(): string | null | undefined {\n return this._email;\n }\n\n /**\n * Gets the auto-approval setting.\n */\n get autoApprove(): boolean | null | undefined {\n return this._autoApprove;\n }\n\n /**\n * Gets the user's name for display purposes.\n * Returns the display name if it exists, otherwise returns the first and last name combined.\n */\n getName(): string {\n if (this._displayName && this._displayName.trim() !== \"\") {\n return this._displayName;\n }\n\n const firstName = this._firstName ?? \"\";\n const lastName = this._lastName ?? \"\";\n\n return `${firstName} ${lastName}`.trim();\n }\n\n /**\n * Creates a plain object representation of the profile for validation or serialization.\n *\n * @returns A plain object matching the ProfileType interface\n */\n toObject(): ProfileDTO {\n return {\n id: this._id,\n firstName: this._firstName,\n lastName: this._lastName,\n displayName: this._displayName,\n phone: this._phone,\n accountNo: this._accountNo,\n email: this._email,\n autoApprove: this._autoApprove,\n };\n }\n\n /**\n * Converts the profile to a JSON string.\n *\n * @returns A JSON string representation of the profile\n */\n toJSON(): string {\n return JSON.stringify(this.toObject());\n }\n\n /**\n * Validates the profile data against the Zod schema.\n *\n * @returns True if the profile is valid, false otherwise\n */\n validate(): boolean {\n try {\n const result = Profile.schema.safeParse(this.toObject());\n return result.success;\n } catch (error) {\n console.error(\"Profile validation error:\", error);\n return false;\n }\n }\n\n /**\n * Creates a Profile instance from a JSON string.\n *\n * @param jsonString - JSON string containing profile data\n * @returns A new Profile instance, or undefined if parsing failed\n */\n static fromJSON(jsonString: string): Profile | undefined {\n try {\n const data = JSON.parse(jsonString);\n return Profile.from(data);\n } catch (error) {\n console.error(\"Error parsing profile JSON:\", error);\n return undefined;\n }\n }\n\n /**\n * Creates a Profile instance from a plain object.\n *\n * @param data - Object containing profile data\n * @returns A new Profile instance, or undefined if parsing failed\n */\n // deno-lint-ignore no-explicit-any\n static from(data: any): Profile | undefined {\n try {\n if (!data) {\n console.error(\"Data is null or undefined\");\n return undefined;\n }\n\n if (typeof data !== \"object\") {\n console.error(\"Data is not an object\");\n return undefined;\n }\n\n if (!data.id || !data.accountNo || !data.displayName) {\n console.error(\"Missing required profile fields\");\n return undefined;\n }\n\n return Profile.create({\n id: data.id,\n firstName: data.firstName,\n lastName: data.lastName,\n displayName: data.displayName,\n phone: data.phone,\n accountNo: data.accountNo,\n email: data.email,\n autoApprove: data.autoApprove,\n });\n } catch (error) {\n console.error(\"Error creating profile from object:\", error);\n return undefined;\n }\n }\n\n /**\n * Type guard to check if an unknown object is a valid Profile instance.\n *\n * @param obj - The object to check\n * @returns Type predicate indicating if the object is a valid Profile\n */\n static is(obj: unknown): obj is Profile {\n if (!obj || typeof obj !== \"object\") return false;\n\n const maybeProfile = obj as Record<string, unknown>;\n\n // Check required properties\n if (\n typeof maybeProfile._id !== \"string\" ||\n typeof maybeProfile._displayName !== \"string\" ||\n typeof maybeProfile._accountNo !== \"string\"\n ) {\n return false;\n }\n\n // Check nullable/optional properties have the right type when present\n if (\n maybeProfile._firstName !== null &&\n maybeProfile._firstName !== undefined &&\n typeof maybeProfile._firstName !== \"string\"\n ) {\n return false;\n }\n\n if (\n maybeProfile._lastName !== null &&\n maybeProfile._lastName !== undefined &&\n typeof maybeProfile._lastName !== \"string\"\n ) {\n return false;\n }\n\n if (maybeProfile._email !== null && maybeProfile._email !== undefined && typeof maybeProfile._email !== \"string\") {\n return false;\n }\n\n // Check phone number\n const phone = maybeProfile._phone;\n if (phone !== null && phone !== undefined && typeof phone !== \"string\") {\n return false;\n }\n\n // Check autoApprove\n const autoApprove = maybeProfile._autoApprove;\n if (autoApprove !== null && autoApprove !== undefined && typeof autoApprove !== \"boolean\") {\n return false;\n }\n\n return true;\n }\n}","import { Permissions } from \"./permission\";\nimport { Profile } from \"./profile.model\";\n\n/**\n * Represents a user in Afloat.\n *\n * This class centralizes user-related logic, simplifying interaction\n * with user-related data and ensuring consistent access checks across the application.\n */\nexport class User {\n /**\n * Logged-in user name.\n */\n public name: string;\n\n /**\n * Logged-in identity: phone number or email address.\n */\n public identity: string;\n\n /**\n * The user's Afloat profile.\n */\n public profile: Profile;\n\n /**\n * The user's authentication token.\n */\n public token: string;\n\n /**\n * Indicates whether the user must change their default password.\n */\n public resetPassword: boolean;\n\n /**\n * A list of granted access keys (permissions).\n */\n public access: string[];\n\n /**\n * A map of access keys to boolean values for fast permission checks.\n */\n private accessMap: Record<string, boolean>;\n\n /**\n * Creates a new `User` instance.\n */\n private constructor(data: {\n profile: Profile;\n token: string;\n access: string[];\n resetPassword: boolean;\n name: string;\n identity: string;\n }) {\n const { profile, token, access, resetPassword, name, identity } = data;\n\n this.profile = profile;\n this.token = token;\n this.resetPassword = resetPassword;\n this.name = name;\n this.identity = identity;\n this.access = access;\n\n // Initialize the access map\n this.accessMap = {};\n for (const permission of Object.values(Permissions)) {\n if (typeof permission === \"object\") {\n Object.values(permission).forEach((perm) => {\n this.accessMap[perm] = access.includes(perm);\n });\n } else {\n this.accessMap[permission] = access.includes(permission);\n }\n }\n }\n\n /**\n * Checks if the user has a specific access permission.\n *\n * @param key - The access key to check.\n * @returns `true` if the user has the specified access, otherwise `false`.\n */\n public can(key: string): boolean {\n return this.accessMap[key] ?? false;\n }\n\n /**\n * Returns a plain object representation of the user.\n * This can safely be embedded in a JWT payload.\n */\n public toObject(): Record<string, any> {\n return {\n profile: this.profile.toObject(),\n token: this.token,\n resetPassword: this.resetPassword,\n name: this.name,\n identity: this.identity,\n access: this.access,\n };\n }\n\n /**\n * Serializes the `User` instance to a JSON string.\n */\n public toJSON(): string {\n return JSON.stringify(this.toObject());\n }\n\n /**\n * Creates a new `User` instance from a JSON string.\n */\n public static fromJSON(jsonString: string): User | undefined {\n try {\n return User.from(JSON.parse(jsonString));\n } catch (e) {\n console.error(\"Invalid JSON string:\", e);\n return undefined;\n }\n }\n\n /**\n * Creates a new `User` instance from a plain object.\n */\n public static from(data: any): User | undefined {\n let parsedData: any;\n\n // Parse string if necessary\n if (typeof data === \"string\") {\n try {\n parsedData = JSON.parse(data);\n } catch (error) {\n console.error(\"Invalid JSON string:\", error);\n return undefined;\n }\n } else {\n parsedData = data;\n }\n\n if (!parsedData) {\n console.error(\"Data is null or undefined.\");\n return undefined;\n }\n\n // Handle profile\n let profile: Profile | undefined;\n let rawProfile: any = parsedData.profile;\n\n if (typeof rawProfile === \"string\") {\n try {\n rawProfile = JSON.parse(rawProfile);\n } catch (error) {\n console.error(\"Failed to parse profile JSON string:\", error);\n return undefined;\n }\n }\n\n if (Profile.is(rawProfile)) {\n profile = rawProfile;\n } else if (typeof rawProfile === \"object\" && rawProfile !== null) {\n profile = Profile.from(rawProfile);\n if (!profile) {\n console.error(\"Failed to create Profile from data:\", rawProfile);\n return undefined;\n }\n } else {\n console.error(\"Invalid profile format:\", typeof rawProfile);\n return undefined;\n }\n\n // Validate required fields\n if (\n !parsedData.token ||\n typeof parsedData.token !== \"string\" ||\n !parsedData.name ||\n typeof parsedData.name !== \"string\" ||\n !parsedData.identity ||\n typeof parsedData.identity !== \"string\" ||\n !Array.isArray(parsedData.access) ||\n typeof parsedData.resetPassword !== \"boolean\"\n ) {\n console.error(\"Missing or invalid required User fields:\", {\n token: typeof parsedData.token,\n name: typeof parsedData.name,\n identity: typeof parsedData.identity,\n access: Array.isArray(parsedData.access),\n resetPassword: typeof parsedData.resetPassword,\n });\n return undefined;\n }\n\n return new User({\n profile,\n token: parsedData.token,\n access: parsedData.access,\n resetPassword: parsedData.resetPassword,\n name: parsedData.name,\n identity: parsedData.identity,\n });\n }\n}\n","import {\n ISO2CountryCodesSet,\n Currency,\n CurrencyCodesSet,\n CountryValidation,\n CurrencyValidation,\n} from \"@temboplus/frontend-core\";\nimport { z } from \"zod\";\n\n/**\n * Zod schema definition for validating Wallet data structures.\n * Ensures data integrity for wallet objects, including runtime validation\n * of country codes against the imported ISO2CountryCodesSet.\n */\nconst walletSchema = z.object({\n id: z.string().min(1, { message: \"Wallet ID is required.\" }),\n profileId: z.string().min(1, { message: \"Profile ID is required.\" }),\n accountNo: z.string().min(1, { message: \"Account number is required.\" }),\n accountName: z.string().min(1, { message: \"Account name is required.\" }),\n channel: z.string().min(1, { message: \"Channel is required.\" }),\n // Validate countryCode as a string present in the imported Set\n countryCode: z\n .string()\n .default(\"TZ\")\n .refine((code) => CountryValidation.isISO2CountryCode(code), {\n message: \"Provided country code is not a valid ISO2 code.\",\n }),\n // Validate currencyCode as a string present in the imported Set\n currencyCode: z\n .string()\n .default(\"TZS\")\n .refine((code) => CurrencyValidation.isCurrencyCode(code), {\n message: \"Provided currency code is not a valid currency code.\",\n }),\n createdAt: z.string().datetime({\n message: \"Creation timestamp must be a valid ISO 8601 datetime string.\",\n }),\n updatedAt: z.string().datetime({\n message: \"Update timestamp must be a valid ISO 8601 datetime string.\",\n }),\n});\n\n/**\n * Zod schema definition for validating Wallet data structures.\n * Ensures data integrity for wallet objects, including runtime validation\n * of country codes against the imported ISO2CountryCodesSet.\n */\nconst walletQuerySchema = z.object({\n id: z.string().optional(),\n profileId: z.string().optional(),\n accountNo: z.string().optional(),\n accountName: z.string().optional(),\n channel: z.string().optional(),\n // Validate countryCode as a string present in the imported Set\n countryCode: z\n .string()\n .default(\"TZ\")\n .refine((code) => ISO2CountryCodesSet.has(code), { message: \"Provided country code is not a valid ISO2 code.\" })\n .optional(),\n // Validate currencyCode as a string present in the imported Set\n currencyCode: z\n .string()\n .default(\"TZS\")\n .refine(\n (code) => {\n const currency = Currency.from(code);\n return currency !== undefined && CurrencyCodesSet.has(currency.code);\n },\n { message: \"Provided currency code is not a valid currency code.\" }\n )\n .optional(),\n});\n\n/**\n * Helper function to make a field optional with undefined\n * Transforms null values to undefined for consistency\n * @param schema - The Zod schema to make optional\n * @returns A schema that only allows string or undefined (no null)\n */\nconst makeOptional = <T extends z.ZodType>(schema: T) => schema.optional().transform((val) => val ?? undefined);\n\n/**\n * Schema definition for a statement entry.\n */\nconst statementEntrySchema = z.object({\n accountNo: makeOptional(z.string()),\n debitOrCredit: z.string().min(1, \"Transaction type is required\"),\n tranRefNo: z.string().min(1, \"Transaction reference is required\"),\n narration: z.string().min(1, \"Transaction description is required\"),\n txnDate: z.coerce.date({\n errorMap: () => ({ message: \"Invalid transaction date format\" }),\n }),\n valueDate: z.coerce.date({\n errorMap: () => ({ message: \"Invalid value date format\" }),\n }),\n amountCredited: z.number().min(0, \"Credited amount must be non-negative\"),\n amountDebited: z.number().min(0, \"Debited amount must be non-negative\"),\n balance: z.number(),\n currencyCode: makeOptional(z.string().min(3, \"Currency code must be at least 3 characters\")).default(\"TZS\"),\n});\n\n/**\n * Collection of wallet-related schemas for export.\n * Provides access to both wallet and statement entry validation schemas.\n */\nexport const WalletDTOSchemas = {\n walletDTO: walletSchema,\n walletQuery: walletQuerySchema,\n statementEntry: statementEntrySchema,\n};\n\nexport type WalletDTO = z.infer<typeof walletSchema>;\nexport type WalletQueryDTO = z.infer<typeof walletQuerySchema>;\nexport type WalletStatementEntryDTO = z.infer<typeof statementEntrySchema>;\n","import {\n CountryValidation,\n type CurrencyCode,\n CurrencyValidation,\n type ISO2CountryCode,\n} from \"@temboplus/frontend-core\";\nimport { WalletDTO, WalletDTOSchemas } from \"@/features/wallet/wallet.dtos\";\n\n/**\n * Represents a digital Wallet entity.\n *\n * Provides methods for creating, validating, and accessing wallet data,\n * leveraging Zod for schema validation including runtime checks for country codes.\n */\nexport class Wallet {\n private readonly _id: string;\n private readonly _profileId: string;\n private readonly _accountNo: string;\n private readonly _accountName: string;\n private readonly _channel: string;\n private readonly _countryCode: ISO2CountryCode;\n private readonly _currencyCode: CurrencyCode;\n private readonly _createdAt: string; // Stored as ISO 8601 string\n private readonly _updatedAt: string; // Stored as ISO 8601 string\n\n /**\n * Retrieves the Zod schema used for Wallet validation.\n * @returns The Zod schema object for Wallet.\n */\n static get schema(): typeof WalletDTOSchemas.walletDTO {\n return WalletDTOSchemas.walletDTO;\n }\n\n /**\n * Private constructor enforces instantiation via static factory methods.\n * @param data - The validated wallet data object (countryCode is string).\n */\n private constructor(data: WalletDTO) {\n this._id = data.id;\n this._profileId = data.profileId;\n this._accountNo = data.accountNo;\n this._accountName = data.accountName;\n this._channel = data.channel;\n this._countryCode = data.countryCode as ISO2CountryCode; // Assigns the validated string\n this._currencyCode = data.currencyCode as CurrencyCode;\n this._createdAt = data.createdAt;\n this._updatedAt = data.updatedAt;\n }\n\n /**\n * Creates a new Wallet instance from provided data after validation.\n * Validation includes checking if the countryCode exists in ISO2CountryCodesSet.\n *\n * @param data - An object containing the necessary properties for a Wallet.\n * @returns A new Wallet instance if validation succeeds, otherwise undefined.\n */\n static create(data: {\n id: string;\n profileId: string;\n accountNo: string;\n accountName: string;\n channel: string;\n countryCode: string; // Expecting string, validation done by schema\n currencyCode: string; // Expecting string, validation done by schema\n createdAt: string;\n updatedAt: string;\n }): Wallet | undefined {\n const validationResult = Wallet.schema.safeParse(data);\n\n if (!validationResult.success) {\n console.error(\"Wallet data validation failed:\", validationResult.error.flatten());\n return undefined;\n }\n\n // validationResult.data contains the validated data, including the countryCode string\n return new Wallet(validationResult.data);\n }\n\n // --- Public Accessors ---\n\n get id(): string {\n return this._id;\n }\n get profileId(): string {\n return this._profileId;\n }\n get accountNo(): string {\n return this._accountNo;\n }\n get accountName(): string {\n return this._accountName;\n }\n get channel(): string {\n return this._channel;\n }\n get countryCode(): ISO2CountryCode {\n return this._countryCode;\n }\n get currencyCode(): CurrencyCode {\n return this._currencyCode;\n }\n get createdAt(): string {\n return this._createdAt;\n }\n get updatedAt(): string {\n return this._updatedAt;\n }\n\n /**\n * Provides the creation timestamp as a Date object.\n * @returns The creation Date object.\n */\n get createdAtDate(): Date {\n return new Date(this._createdAt);\n }\n\n /**\n * Provides the last update timestamp as a Date object.\n * @returns The last update Date object.\n */\n get updatedAtDate(): Date {\n return new Date(this._updatedAt);\n }\n\n // --- Instance Methods ---\n\n /**\n * Creates a plain data object representation of the Wallet instance.\n * Suitable for serialization or validation.\n * @returns A plain object conforming to the WalletDataType structure.\n */\n toObject(): WalletDTO {\n return {\n id: this._id,\n profileId: this._profileId,\n accountNo: this._accountNo,\n accountName: this._accountName,\n channel: this._channel,\n countryCode: this._countryCode,\n createdAt: this._createdAt,\n updatedAt: this._updatedAt,\n currencyCode: this._currencyCode,\n };\n }\n\n /**\n * Serializes the Wallet instance into a JSON string.\n * @returns A JSON string representation of the wallet data.\n */\n toJSON(): string {\n return JSON.stringify(this.toObject());\n }\n\n /**\n * Validates the Wallet instance's current data against the defined schema.\n * This includes checking the countryCode against ISO2CountryCodesSet.\n * @returns True if the current instance data is valid, otherwise false.\n */\n validate(): boolean {\n const result = Wallet.schema.safeParse(this.toObject());\n if (!result.success) {\n console.warn(\"Wallet instance validation failed:\", result.error.flatten());\n }\n return result.success;\n }\n\n // --- Static Factory Methods ---\n\n /**\n * Creates a Wallet instance from a JSON string.\n * Performs parsing and validation, including the countryCode runtime check.\n * @param jsonString - A JSON string containing wallet data.\n * @returns A new Wallet instance if parsing and validation succeed, otherwise undefined.\n */\n static fromJSON(jsonString: string): Wallet | undefined {\n try {\n const data = JSON.parse(jsonString);\n return Wallet.from(data);\n } catch (error) {\n console.error(\"Error parsing wallet JSON:\", error);\n return undefined;\n }\n }\n\n /**\n * Creates a Wallet instance from a plain JavaScript object.\n * Validates the input object using the Wallet schema, including the countryCode runtime check.\n * @param data - An object potentially containing wallet data.\n * @returns A new Wallet instance if the object is valid, otherwise undefined.\n */\n // deno-lint-ignore no-explicit-any\n static from(data: any): Wallet | undefined {\n if (!data || typeof data !== \"object\") {\n console.error(\"Invalid data provided to Wallet.from: Input must be an object.\");\n return undefined;\n }\n // Delegate validation (including refinement) and instantiation to 'create'\n return Wallet.create({\n id: data.id,\n profileId: data.profileId,\n accountNo: data.accountNo,\n accountName: data.accountName,\n channel: data.channel,\n countryCode: data.countryCode, // Pass countryCode string\n createdAt: data.createdAt,\n updatedAt: data.updatedAt,\n currencyCode: data.currencyCode,\n });\n }\n\n /**\n * Type guard to check if an unknown object is structurally equivalent\n * to a Wallet instance (duck typing). Includes runtime check against ISO2CountryCodesSet.\n * @param obj - The object to check.\n * @returns True if the object resembles a Wallet instance, false otherwise.\n */\n static is(obj: unknown): obj is Wallet {\n if (!obj || typeof obj !== \"object\") {\n return false;\n }\n\n const maybeWallet = obj as Record<string, unknown>;\n\n return (\n typeof maybeWallet.id === \"string\" &&\n typeof maybeWallet.profileId === \"string\" &&\n typeof maybeWallet.accountNo === \"string\" &&\n typeof maybeWallet.accountName === \"string\" &&\n typeof maybeWallet.channel === \"string\" &&\n // Check if countryCode is a string and exists in the runtime Set\n typeof maybeWallet.countryCode === \"string\" &&\n CountryValidation.isISO2CountryCode(maybeWallet.countryCode) &&\n // Check if currencyCode is a string and exists in the runtime Set\n typeof maybeWallet.currencyCode === \"string\" &&\n CurrencyValidation.isCurrencyCode(maybeWallet.currencyCode) &&\n typeof maybeWallet.createdAt === \"string\" &&\n typeof maybeWallet.updatedAt === \"string\" &&\n typeof maybeWallet.toObject === \"function\" &&\n typeof maybeWallet.validate === \"function\"\n );\n }\n}\n","import { Bank, BankService, BankValidation, TZMobileNumber } from \"@temboplus/frontend-core\";\nimport { BankContactInfo, ContactInfo, MobileContactInfo } from \"./contact-info.model\";\n\n/** Prefix for Ecobank mobile transfer narrations */\nexport const ECOBANK_PREFIX = \"MOBILE TRANSFER \";\n\n/** V2 format prefix for payout narrations using contact details */\nexport const NARR_V2_PREFIX: string = \"PAYOUT\";\n/** V1 format prefix for bank payout narrations */\nexport const BANK_NARR_PREFIX: string = \"PAYOUT TO BANK\";\n/** Legacy format prefix for bank payout narrations */\nexport const LEGACY_BANK_NARR_PREFIX: string = \"TO_BANK\";\n\n/** V1 format prefix for mobile money payout narrations */\nexport const MOBILE_NARR_PREFIX: string = \"PAYOUT TO MOBILE\";\n/** Legacy format prefix for mobile money payout narrations */\nexport const LEGACY_MOBILE_NARR_PREFIX: string = \"TO_MOMO\";\n\n/**\n * Interface for the JSON representation of Narration\n */\nexport interface NarrationJson {\n text: string;\n version?: string;\n}\n\n/**\n * Handles payout narration generation and parsing for the Afloat platform.\n *\n * This class provides functionality to:\n * - Generate standardized payout narrations using contact details (V2 format)\n * - Parse existing narrations to extract contact information\n * - Format narration text for different display contexts\n * - Handle V2 (PAY format), V1, and legacy narration formats for backward compatibility\n * - Serialize/deserialize to/from JSON format\n *\n * @example\n * ```typescript\n * // Generate V2 narration using contact info\n * const mobileContact = new MobileContactInfo(\"John Doe\", phoneNumber);\n * const narration = Narration.generateDefaultPayoutNarration(mobileContact);\n * // Result: \"PAYOUT +255123456789 JOHN DOE\"\n *\n * const bankContact = new BankContactInfo(\"Jane Smith\", bank, \"1234567890\");\n * const narration = Narration.generateDefaultPayoutNarration(bankContact);\n * // Result: \"PAYOUT CORUTZTZ 1234567890 JANE SMITH\"\n *\n * // Extract contact details from narration\n * const existingNarration = new Narration(\"PAYOUT +255123456789 JOHN DOE\");\n * const contactInfo = existingNarration.getContactDetails();\n * // Returns: MobileContactInfo instance\n * ```\n */\nexport class Narration {\n /** The raw narration text */\n text: string;\n\n /**\n * Creates a new Narration instance.\n *\n * @param text - The narration text to wrap\n */\n constructor(text: string) {\n this.text = text;\n }\n\n /**\n * Returns a medium-length version of the narration text suitable for table columns.\n * Truncates to 47 characters + \"...\" if longer than 50 characters.\n *\n * @returns Truncated narration text for medium-width displays\n */\n get mediumText() {\n if (this.text.length > 50) {\n return this.text.substring(0, 47) + \"...\";\n }\n\n return this.text;\n }\n\n /**\n * Returns a short version of the narration text suitable for compact displays.\n * Truncates to 32 characters + \"...\" if longer than 35 characters.\n *\n * @returns Truncated narration text for narrow displays\n */\n get shortText() {\n if (this.text.length > 35) {\n return this.text.substring(0, 32) + \"...\";\n }\n\n return this.text;\n }\n\n /**\n * Generates the default payout narration based on contact information using the V2 format.\n * Automatically determines whether to generate mobile or bank narration based on contact type.\n *\n * @param data - Contact information (either MobileContactInfo or BankContactInfo)\n * @returns Formatted default narration string in uppercase using V2 format, or empty string if contact type is unrecognized\n *\n * @example\n * ```typescript\n * const mobileContact = new MobileContactInfo(\"John Doe\", phoneNumber);\n * const narration = Narration.generateDefaultPayoutNarration(mobileContact);\n * // Returns: \"PAYOUT +255123456789 JOHN DOE\"\n *\n * const bankContact = new BankContactInfo(\"Jane Smith\", bank, \"1234567890\");\n * const narration = Narration.generateDefaultPayoutNarration(bankContact);\n * // Returns: \"PAYOUT CORUTZTZ 1234567890 JANE SMITH\"\n * ```\n */\n static generateDefaultPayoutNarration(data: ContactInfo): string {\n if (MobileContactInfo.is(data)) {\n return Narration.generateMobilePayoutNarrationV2(data);\n } else if (BankContactInfo.is(data)) {\n return Narration.generateBankPayoutNarrationV2(data);\n } else {\n return \"\";\n }\n }\n\n /**\n * Generates a V2 standardized mobile money payout narration with contact details.\n * Format: \"PAY {phone_number} {name}\"\n *\n * @param data - Mobile contact information containing phone number and name\n * @returns Formatted V2 mobile payout narration in uppercase\n *\n * @example\n * ```typescript\n * const contact = new MobileContactInfo(\"John Doe\", phoneNumber);\n * const narration = Narration.generateMobilePayoutNarrationV2(contact);\n * // Returns: \"PAYOUT +255123456789 JOHN DOE\"\n * ```\n */\n static generateMobilePayoutNarrationV2(data: MobileContactInfo): string {\n const { phoneNumber, name } = data;\n return `${NARR_V2_PREFIX.toUpperCase()} ${phoneNumber.e164Format.trim()} ${name.trim()}`.toUpperCase();\n }\n\n /**\n * Generates a V2 standardized bank payout narration with contact details.\n * Format: \"PAY {swift_code} {account_number} {account_name}\"\n *\n * @param data - Bank contact information containing bank details, account number, and account name\n * @returns Formatted V2 bank payout narration in uppercase\n *\n * @example\n * ```typescript\n * const contact = new BankContactInfo(\"Jane Smith\", bank, \"1234567890\");\n * const narration = Narration.generateBankPayoutNarrationV2(contact);\n * // Returns: \"PAYOUT CORUTZTZ 1234567890 JANE SMITH\"\n * ```\n */\n static generateBankPayoutNarrationV2(data: BankContactInfo): string {\n const { bank, accName, accNo } = data;\n return `${NARR_V2_PREFIX.toUpperCase()} ${bank.swiftCode.trim()} ${accNo.trim()} ${accName.trim()}`.toUpperCase();\n }\n\n /**\n * Extracts contact information from the narration text.\n * Supports V2 (PAY format), V1, and legacy formats for comprehensive contact detection.\n *\n * @returns Parsed ContactInfo (BankContactInfo or MobileContactInfo) if successful, undefined otherwise\n *\n * @example\n * ```typescript\n * // V2 format\n * const v2Narration = new Narration(\"PAYOUT +255123456789 JOHN DOE\");\n * const v2Contact = v2Narration.getContactDetails();\n * // Returns: MobileContactInfo instance\n *\n * // V1 format\n * const v1Narration = new Narration(\"PAYOUT TO BANK CRDB 1234567890 Jane Smith\");\n * const v1Contact = v1Narration.getContactDetails();\n * // Returns: BankContactInfo instance\n * ```\n */\n getContactDetails = (): ContactInfo | undefined => {\n const bankResult = this.getBankContactDetails();\n const mobileResult = this.getMobileContactDetails();\n\n if (bankResult) return bankResult;\n if (mobileResult) return mobileResult;\n };\n\n /**\n * Extracts bank contact information from the narration text.\n * Handles V2 format (PAY with SWIFT code and details), V1 format, and legacy format.\n * Also handles Ecobank-prefixed narrations by stripping the prefix.\n *\n * V2 format: \"PAYOUT {swift_code} {account_number} {account_name}\"\n * V1 format: \"PAYOUT TO BANK {bank_name} {account_number} {account_name}\"\n * Legacy format: \"TO_BANK => {json_object}\"\n *\n * @returns BankContactInfo if parsing is successful, undefined otherwise\n *\n * @example\n * ```typescript\n * // V2 format\n * const v2Narration = new Narration(\"PAYOUT CORUTZTZ 1234567890 JANE SMITH\");\n * const v2Contact = v2Narration.getBankContactDetails();\n *\n * // V1 format\n * const v1Narration = new Narration(\"PAYOUT TO BANK CRDB 1234567890 Jane Smith\");\n * const v1Contact = v1Narration.getBankContactDetails();\n * ```\n */\n getBankContactDetails = (): BankContactInfo | undefined => {\n let narr = this.text.trim();\n\n if (narr.startsWith(ECOBANK_PREFIX)) {\n narr = narr.substring(ECOBANK_PREFIX.length);\n }\n\n try {\n // Handle V2 format: \"PAYOUT {swift_code} {account_number} {account_name}\"\n if (narr.startsWith(NARR_V2_PREFIX)) {\n const data = narr.replace(NARR_V2_PREFIX, \"\").trim().split(\" \");\n\n if (data.length >= 3) {\n const potentialSwift = data[0];\n const accNo = data[1];\n const accName = data.slice(2).map(capitalizeFirstLetter).join(\" \");\n\n // Check if first parameter looks like a SWIFT code (8-11 chars, letters+numbers)\n if (potentialSwift.length >= 8 && potentialSwift.length <= 11 && /^[A-Z0-9]+$/i.test(potentialSwift)) {\n const country = BankValidation.getCountryFromSwiftCode(potentialSwift);\n if (country) {\n const bankCode = BankValidation.validateSwiftCode(potentialSwift, country);\n if (bankCode) {\n const bank = Bank.from(potentialSwift, country);\n if (accNo && accName && bank) {\n return new BankContactInfo(accName, bank, accNo);\n }\n }\n }\n }\n }\n }\n\n // Handle legacy format: \"TO_BANK => {json}\"\n if (narr.startsWith(LEGACY_BANK_NARR_PREFIX)) {\n const json = narr.split(\"=>\")[1].trim();\n const map = JSON.parse(json) as Record<string, string>;\n const accNo = map[\"account_number\"];\n const accName = map[\"account_name\"];\n const code = map[\"swift_code\"];\n\n const country = BankValidation.getCountryFromSwiftCode(code);\n if (!country) {\n return undefined;\n }\n\n const bankCode = BankValidation.validateSwiftCode(code, country);\n if (!bankCode) {\n return undefined;\n }\n\n const bank = Bank.from(code, country);\n\n if (accNo && accName && bank) {\n return new BankContactInfo(accName, bank, accNo);\n }\n }\n\n // Handle V1 format: \"PAYOUT TO BANK {bank_name} {account_number} {account_name}\"\n if (narr.startsWith(BANK_NARR_PREFIX)) {\n const data = narr.replace(BANK_NARR_PREFIX, \"\").trim().split(\" \");\n const banks = BankService.getInstance().searchBanks(\"TZ\", data[0]);\n const bank = banks.length > 0 ? banks[0] : undefined;\n const accNo = data[1];\n const accName = data.slice(2).map(capitalizeFirstLetter).join(\" \");\n\n if (accName && accNo && bank) {\n return new BankContactInfo(accName, bank, accNo);\n }\n }\n } catch (_) {\n return undefined;\n }\n\n return undefined;\n };\n\n /**\n * Extracts mobile contact information from the narration text.\n * Handles V2 format (PAY with phone number and name), V1 format, and legacy format.\n * Also handles Ecobank-prefixed narrations by stripping the prefix.\n *\n * V2 format: \"PAYOUT {phone_number} {name}\"\n * V1 format: \"PAYOUT TO MOBILE {phone_number} {name}\"\n * Legacy format: \"TO_MOMO => {json_object}\"\n *\n * @returns MobileContactInfo if parsing is successful, undefined otherwise\n *\n * @example\n * ```typescript\n * // V2 format\n * const v2Narration = new Narration(\"PAYOUT +255123456789 JOHN DOE\");\n * const v2Contact = v2Narration.getMobileContactDetails();\n *\n * // V1 format\n * const v1Narration = new Narration(\"PAYOUT TO MOBILE +255123456789 John Doe\");\n * const v1Contact = v1Narration.getMobileContactDetails();\n * ```\n */\n getMobileContactDetails = (): MobileContactInfo | undefined => {\n let narr = this.text.trim();\n\n if (narr.startsWith(ECOBANK_PREFIX)) {\n narr = narr.substring(ECOBANK_PREFIX.length);\n }\n\n try {\n // Handle V2 format: \"PAY {phone_number} {name}\"\n if (narr.startsWith(NARR_V2_PREFIX)) {\n const data = narr.replace(NARR_V2_PREFIX, \"\").trim().split(\" \");\n\n if (data.length >= 2) {\n const potentialPhone = data[0];\n const username = data.slice(1).map(capitalizeFirstLetter).join(\" \");\n\n // Check if first parameter looks like a phone number (starts with +)\n if (potentialPhone.startsWith(\"+\")) {\n const phone = TZMobileNumber.from(potentialPhone);\n\n if (phone && username) {\n return new MobileContactInfo(username, phone);\n }\n }\n }\n }\n\n // Handle legacy format: \"TO_MOMO => {json}\"\n if (narr.startsWith(LEGACY_MOBILE_NARR_PREFIX)) {\n const json = narr.split(\"=>\")[1].trim();\n const map = JSON.parse(json) as Record<string, string>;\n const phoneNumber = TZMobileNumber.from(map[\"phone_number\"]);\n let username = map[\"username\"];\n if (username === undefined) username = \"\";\n\n // Clean up username: remove extra spaces and capitalize each word\n let names = username.split(\" \");\n names = names.filter((n) => n.trim().length > 0);\n username = names.map(capitalizeFirstLetter).join(\" \");\n\n if (phoneNumber && username) {\n return new MobileContactInfo(username, phoneNumber);\n }\n }\n\n // Handle V1 format: \"PAYOUT TO MOBILE {phone_number} {name}\"\n if (narr.startsWith(MOBILE_NARR_PREFIX)) {\n const data = narr.replace(MOBILE_NARR_PREFIX, \"\").trim().split(\" \");\n const phone = TZMobileNumber.from(data[0]);\n const username = data.slice(1).map(capitalizeFirstLetter).join(\" \");\n\n if (phone && username) {\n return new MobileContactInfo(username, phone);\n }\n }\n } catch (_) {\n return undefined;\n }\n\n return undefined;\n };\n\n /**\n * Serializes the Narration instance to a JSON-compatible object.\n *\n * @returns {NarrationJson} A plain object containing the narration data\n *\n * @example\n * ```typescript\n * const narration = new Narration(\"PAYOUT +255123456789 JOHN DOE\");\n * const json = narration.toJson();\n * // Returns: { text: \"PAYOUT +255123456789 JOHN DOE\", version: \"2.0\" }\n * ```\n */\n toJson(): NarrationJson {\n return {\n text: this.text,\n version: \"2.0\",\n };\n }\n\n /**\n * Creates a Narration instance from a JSON-compatible object.\n *\n * @param {NarrationJson | string} json - Either a NarrationJson object or a JSON string\n * @returns {Narration | undefined} A Narration instance if valid, undefined otherwise\n *\n * @example\n * ```typescript\n * // From object\n * const narration = Narration.fromJson({ text: \"PAYOUT +255123456789 JOHN DOE\" });\n *\n * // From JSON string\n * const narration = Narration.fromJson('{\"text\":\"PAYOUT +255123456789 JOHN DOE\"}');\n * ```\n */\n static fromJson(json: NarrationJson | string): Narration | undefined {\n try {\n // Parse JSON string if provided\n let data: NarrationJson;\n if (typeof json === \"string\") {\n data = JSON.parse(json);\n } else {\n data = json;\n }\n\n // Validate required properties\n if (!data || typeof data !== \"object\" || typeof data.text !== \"string\") {\n console.warn(\"Invalid NarrationJson: missing or invalid text property\");\n return undefined;\n }\n\n return new Narration(data.text);\n } catch (error) {\n console.error(\"Error parsing NarrationJson:\", error);\n return undefined;\n }\n }\n\n /**\n * Type guard to check if an object is a valid NarrationJson\n *\n * @param obj - The object to validate\n * @returns True if the object conforms to NarrationJson structure\n */\n static isNarrationJson(obj: unknown): obj is NarrationJson {\n if (!obj || typeof obj !== \"object\") return false;\n\n const maybeJson = obj as Record<string, unknown>;\n return typeof maybeJson.text === \"string\";\n }\n\n /**\n * Type guard to check if an unknown object is a valid Narration instance.\n *\n * @param obj - The object to check\n * @returns Type predicate indicating if the object is a Narration instance\n */\n static is(obj: unknown): obj is Narration {\n if (!obj || typeof obj !== \"object\") return false;\n\n const maybeNarration = obj as Narration;\n try {\n return (\n typeof maybeNarration.text === \"string\" &&\n typeof maybeNarration.getContactDetails === \"function\" &&\n typeof maybeNarration.toJson === \"function\"\n );\n } catch (error) {\n return false;\n }\n }\n}\n\n/**\n * Capitalizes the first letter of a string and converts the rest to lowercase.\n * Used for standardizing name formatting in narrations.\n *\n * @param str - The string to capitalize\n * @returns String with first letter capitalized and rest in lowercase\n *\n * @example\n * ```typescript\n * capitalizeFirstLetter(\"john\") // Returns: \"John\"\n * capitalizeFirstLetter(\"SMITH\") // Returns: \"Smith\"\n * capitalizeFirstLetter(\"\") // Returns: \"\"\n * ```\n */\nfunction capitalizeFirstLetter(str: string): string {\n if (str.length === 0) {\n return str; // Return an empty string if input is empty\n }\n\n const firstLetter = str.charAt(0).toUpperCase();\n const restOfString = str.slice(1).toLowerCase();\n\n return firstLetter + restOfString;\n}\n","import { WalletDTOSchemas, WalletStatementEntryDTO } from \"@/features/wallet/wallet.dtos\";\nimport { Amount, AmountJson, Currency } from \"@temboplus/frontend-core\";\nimport { Narration, NarrationJson } from \"./narration.model\";\n\n/**\n * Interface for the JSON representation of WalletStatementEntry\n */\nexport interface WalletStatementEntryJson {\n accountNo?: string;\n debitOrCredit: string;\n tranRefNo: string;\n narration: NarrationJson;\n txnDate: string; // ISO date string\n valueDate: string; // ISO date string\n amountCredited: AmountJson;\n amountDebited: AmountJson;\n balance: AmountJson;\n currencyCode: string;\n version?: string;\n}\n\n/**\n * Represents a single entry in a Wallet's statement history.\n *\n * This class provides methods for creating, validating, and accessing statement entry data.\n * It integrates with the Zod schema validation for data integrity and includes narration parsing capabilities.\n * The narration field now uses the Narration class for enhanced functionality.\n */\nexport class WalletStatementEntry {\n private _accountNo?: string;\n private _debitOrCredit: string;\n private _tranRefNo: string;\n private _narration: Narration;\n private _txnDate: Date;\n private _valueDate: Date;\n private _amountCredited: Amount;\n private _amountDebited: Amount;\n private _balance: Amount;\n private _currencyCode: string;\n\n /**\n * Gets the statement entry schema used for validation.\n */\n static get schema() {\n return WalletDTOSchemas.statementEntry;\n }\n\n /**\n * Private constructor to enforce use of static factory methods.\n * Assumes data is already validated and dates are Date objects.\n * @param data - Object containing statement entry information conforming to WalletStatementEntryDTO.\n * @param narration - Narration instance for the transaction\n */\n private constructor(data: Omit<WalletStatementEntryDTO, \"narration\">, narration: Narration) {\n this._accountNo = data.accountNo;\n this._debitOrCredit = data.debitOrCredit;\n this._tranRefNo = data.tranRefNo;\n this._narration = narration;\n this._txnDate = data.txnDate; // Should be Date object from validation\n this._valueDate = data.valueDate; // Should be Date object from validation\n this._currencyCode = data.currencyCode;\n\n const currency = Currency.from(data.currencyCode);\n if (!currency) {\n throw new Error(`Currency not found for code: ${data.currencyCode}`);\n }\n\n // Create Amount instances with proper validation\n const amountCredited = Amount.from(data.amountCredited, currency.code);\n const amountDebited = Amount.from(data.amountDebited, currency.code);\n const balance = Amount.from(data.balance, currency.code);\n\n if (!amountCredited || !amountDebited || !balance) {\n throw new Error(`Failed to create Amount instances with currency code: ${data.currencyCode}`);\n }\n\n this._amountCredited = amountCredited;\n this._amountDebited = amountDebited;\n this._balance = balance;\n }\n\n /**\n * Creates a new WalletStatementEntry instance after validating the input data.\n * Handles date coercion via the schema and uses currencyCode from the data.\n *\n * @param data - Object containing statement entry information. Dates can be strings or Date objects.\n * @returns A new WalletStatementEntry instance, or undefined if validation fails.\n */\n static create(data: {\n accountNo?: string | null; // Allow null input, schema transforms to undefined\n debitOrCredit: string;\n tranRefNo: string;\n narration: string | Narration;\n txnDate: string | Date; // Allow string or Date input\n valueDate: string | Date; // Allow string or Date input\n amountCredited: number;\n amountDebited: number;\n balance: number;\n currencyCode?: string; // Optional, defaults to \"TZS\"\n }): WalletStatementEntry | undefined {\n // Handle narration input - convert to Narration if string\n const narrationInstance = typeof data.narration === \"string\" ? new Narration(data.narration) : data.narration;\n\n // Create validation data with string narration for schema\n const validationData = {\n ...data,\n narration: narrationInstance.text,\n };\n\n const validationResult = WalletStatementEntry.schema.safeParse(validationData);\n\n if (!validationResult.success) {\n console.error(\"WalletStatementEntry data validation failed:\", validationResult.error.flatten());\n return undefined;\n }\n\n try {\n // Use validated data (dates are coerced to Date objects, optional strings handled)\n const { narration, ...restData } = validationResult.data;\n return new WalletStatementEntry(restData, narrationInstance);\n } catch (error) {\n console.error(\"Failed to create WalletStatementEntry:\", error);\n return undefined;\n }\n }\n\n // --- Getters ---\n get accountNo(): string | undefined {\n return this._accountNo;\n }\n get debitOrCredit(): string {\n return this._debitOrCredit;\n }\n get tranRefNo(): string {\n return this._tranRefNo;\n }\n get narration(): Narration {\n return this._narration;\n }\n get txnDate(): Date {\n return this._txnDate;\n }\n get valueDate(): Date {\n return this._valueDate;\n }\n get amountCredited(): Amount {\n return this._amountCredited;\n }\n get amountDebited(): Amount {\n return this._amountDebited;\n }\n get balance(): Amount {\n return this._balance;\n }\n get currencyCode(): string {\n return this._currencyCode;\n }\n\n /**\n * Serializes the WalletStatementEntry instance to a JSON-compatible object.\n * Dates are converted to ISO strings, amounts are serialized using Amount.toJson(),\n * and narration is serialized using Narration.toJson().\n *\n * @returns {WalletStatementEntryJson} A plain object containing all necessary data\n */\n toJson(): WalletStatementEntryJson {\n return {\n accountNo: this._accountNo,\n debitOrCredit: this._debitOrCredit,\n tranRefNo: this._tranRefNo,\n narration: this._narration.toJson(),\n txnDate: this._txnDate.toISOString(),\n valueDate: this._valueDate.toISOString(),\n amountCredited: this._amountCredited.toJson(),\n amountDebited: this._amountDebited.toJson(),\n balance: this._balance.toJson(),\n currencyCode: this._currencyCode,\n version: \"2.0\", // Updated version to reflect Narration integration\n };\n }\n\n /**\n * Creates a WalletStatementEntry instance from a JSON-compatible object.\n *\n * @param {WalletStatementEntryJson | string} json - Either a WalletStatementEntryJson object or a JSON string\n * @returns {WalletStatementEntry | undefined} A WalletStatementEntry instance if valid, undefined otherwise\n */\n static fromJson(json: WalletStatementEntryJson | string): WalletStatementEntry | undefined {\n try {\n // Parse JSON string if provided\n let data: WalletStatementEntryJson;\n if (typeof json === \"string\") {\n data = JSON.parse(json);\n } else {\n data = json;\n }\n\n // Validate required properties\n if (\n !data ||\n typeof data !== \"object\" ||\n typeof data.debitOrCredit !== \"string\" ||\n typeof data.tranRefNo !== \"string\" ||\n !data.narration ||\n typeof data.txnDate !== \"string\" ||\n typeof data.valueDate !== \"string\" ||\n typeof data.currencyCode !== \"string\" ||\n !data.amountCredited ||\n !data.amountDebited ||\n !data.balance\n ) {\n console.warn(\"Invalid WalletStatementEntryJson: missing or invalid required properties\");\n return undefined;\n }\n\n // Deserialize narration\n const narration = Narration.fromJson(data.narration);\n if (!narration) {\n console.warn(\"Invalid WalletStatementEntryJson: failed to deserialize narration\");\n return undefined;\n }\n\n // Deserialize Amount objects\n const amountCredited = Amount.fromJson(data.amountCredited);\n const amountDebited = Amount.fromJson(data.amountDebited);\n const balance = Amount.fromJson(data.balance);\n\n if (!amountCredited || !amountDebited || !balance) {\n console.warn(\"Invalid WalletStatementEntryJson: failed to deserialize Amount objects\");\n return undefined;\n }\n\n // Validate currency consistency\n if (\n amountCredited.currencyCode !== data.currencyCode ||\n amountDebited.currencyCode !== data.currencyCode ||\n balance.currencyCode !== data.currencyCode\n ) {\n console.warn(\"Invalid WalletStatementEntryJson: currency code mismatch\");\n return undefined;\n }\n\n // Create using the create method with proper validation\n return WalletStatementEntry.create({\n accountNo: data.accountNo,\n debitOrCredit: data.debitOrCredit,\n tranRefNo: data.tranRefNo,\n narration,\n txnDate: data.txnDate, // String date will be coerced by schema\n valueDate: data.valueDate, // String date will be coerced by schema\n amountCredited: amountCredited.numericValue,\n amountDebited: amountDebited.numericValue,\n balance: balance.numericValue,\n currencyCode: data.currencyCode,\n });\n } catch (error) {\n console.error(\"Error parsing WalletStatementEntryJson:\", error);\n return undefined;\n }\n }\n\n /**\n * Type guard to check if an object is a valid WalletStatementEntryJson\n *\n * @param obj - The object to validate\n * @returns True if the object conforms to WalletStatementEntryJson structure\n */\n static isWalletStatementEntryJson(obj: unknown): obj is WalletStatementEntryJson {\n if (!obj || typeof obj !== \"object\") return false;\n\n const maybeJson = obj as Record<string, unknown>;\n\n return (\n typeof maybeJson.debitOrCredit === \"string\" &&\n typeof maybeJson.tranRefNo === \"string\" &&\n Narration.isNarrationJson(maybeJson.narration) &&\n typeof maybeJson.txnDate === \"string\" &&\n typeof maybeJson.valueDate === \"string\" &&\n typeof maybeJson.currencyCode === \"string\" &&\n (maybeJson.accountNo === undefined || typeof maybeJson.accountNo === \"string\") &&\n Amount.isAmountJson(maybeJson.amountCredited) &&\n Amount.isAmountJson(maybeJson.amountDebited) &&\n Amount.isAmountJson(maybeJson.balance)\n );\n }\n\n /**\n * Validates the current statement entry instance data against the schema.\n * @returns True if the statement entry instance data is valid, false otherwise.\n */\n validate(): boolean {\n // Create a validation object with numeric values for schema validation\n const validationData = {\n accountNo: this._accountNo,\n debitOrCredit: this._debitOrCredit,\n tranRefNo: this._tranRefNo,\n narration: this._narration.text,\n txnDate: this._txnDate,\n valueDate: this._valueDate,\n amountCredited: this._amountCredited.numericValue,\n amountDebited: this._amountDebited.numericValue,\n balance: this._balance.numericValue,\n currencyCode: this._currencyCode,\n };\n\n const result = WalletStatementEntry.schema.safeParse(validationData);\n if (!result.success) {\n console.warn(\"WalletStatementEntry instance validation failed:\", result.error.flatten());\n }\n return result.success;\n }\n\n /**\n * Type guard to check if an unknown object is a valid WalletStatementEntry instance.\n * Performs structural checks (duck typing).\n * @param obj - The object to check.\n * @returns Type predicate indicating if the object is a WalletStatementEntry instance.\n */\n static is(obj: unknown): obj is WalletStatementEntry {\n if (!obj || typeof obj !== \"object\") return false;\n\n const maybeEntry = obj as WalletStatementEntry;\n\n try {\n return (\n (typeof maybeEntry.accountNo === \"string\" || maybeEntry.accountNo === undefined) &&\n typeof maybeEntry.debitOrCredit === \"string\" &&\n typeof maybeEntry.tranRefNo === \"string\" &&\n Narration.is(maybeEntry.narration) &&\n maybeEntry.txnDate instanceof Date &&\n maybeEntry.valueDate instanceof Date &&\n Amount.is(maybeEntry.amountCredited) &&\n Amount.is(maybeEntry.amountDebited) &&\n Amount.is(maybeEntry.balance) &&\n typeof maybeEntry.currencyCode === \"string\" &&\n typeof maybeEntry.toJson === \"function\" &&\n typeof maybeEntry.validate === \"function\"\n );\n } catch (error) {\n return false;\n }\n }\n\n /**\n * Checks if two WalletStatementEntry instances are equal based on their transaction reference numbers.\n * @param other - Another WalletStatementEntry to compare with.\n * @returns True if both entries have the same transaction reference number.\n */\n equals(other: WalletStatementEntry): boolean {\n return this._tranRefNo === other._tranRefNo;\n }\n\n /**\n * Creates a copy of this WalletStatementEntry with the same data.\n * @returns A new WalletStatementEntry instance with identical data.\n */\n clone(): WalletStatementEntry | undefined {\n return WalletStatementEntry.create({\n accountNo: this._accountNo,\n debitOrCredit: this._debitOrCredit,\n tranRefNo: this._tranRefNo,\n narration: this._narration, // Narration instances are immutable\n txnDate: this._txnDate,\n valueDate: this._valueDate,\n amountCredited: this._amountCredited.numericValue,\n amountDebited: this._amountDebited.numericValue,\n balance: this._balance.numericValue,\n currencyCode: this._currencyCode,\n });\n }\n\n /**\n * Returns a string representation of the statement entry for debugging.\n * @returns A formatted string with key transaction details.\n */\n toString(): string {\n return `WalletStatementEntry(${this._tranRefNo}: ${this._debitOrCredit} ${\n this._amountCredited.isPositive() ? this._amountCredited.label : this._amountDebited.label\n } on ${this._txnDate.toISOString().split(\"T\")[0]}${this._narration})`;\n }\n\n // Static array methods for bulk operations\n /**\n * Serializes an array of WalletStatementEntry instances to JSON-compatible objects\n *\n * @param entries - Array of WalletStatementEntry instances to serialize\n * @returns Array of WalletStatementEntryJson objects\n */\n static toJsonArray(entries: WalletStatementEntry[]): WalletStatementEntryJson[] {\n return entries.map((entry) => entry.toJson());\n }\n\n /**\n * Creates WalletStatementEntry instances from an array of JSON-compatible objects\n *\n * @param jsonArray - Array of WalletStatementEntryJson objects or JSON string\n * @returns Array of WalletStatementEntry instances (invalid items are filtered out)\n */\n static fromJsonArray(jsonArray: WalletStatementEntryJson[] | string): WalletStatementEntry[] {\n try {\n let data: WalletStatementEntryJson[];\n if (typeof jsonArray === \"string\") {\n data = JSON.parse(jsonArray);\n } else {\n data = jsonArray;\n }\n\n if (!Array.isArray(data)) {\n console.warn(\"fromJsonArray expects an array\");\n return [];\n }\n\n return data\n .map((item) => WalletStatementEntry.fromJson(item))\n .filter((entry): entry is WalletStatementEntry => entry !== undefined);\n } catch (error) {\n console.error(\"Error parsing WalletStatementEntryJson array:\", error);\n return [];\n }\n }\n\n // Legacy methods for backwards compatibility\n /**\n * Legacy method for backwards compatibility - creates object representation\n * @deprecated Use toJson() instead for proper serialization\n */\n toObject(): Omit<WalletStatementEntryDTO, \"amountCredited\" | \"amountDebited\" | \"balance\" | \"narration\"> & {\n amountCredited: AmountJson;\n amountDebited: AmountJson;\n balance: AmountJson;\n narration: string; // Legacy format returns string\n } {\n return {\n accountNo: this._accountNo,\n debitOrCredit: this._debitOrCredit,\n tranRefNo: this._tranRefNo,\n narration: this._narration.text, // Return text for legacy compatibility\n txnDate: this._txnDate,\n valueDate: this._valueDate,\n amountCredited: this._amountCredited.toJson(),\n amountDebited: this._amountDebited.toJson(),\n balance: this._balance.toJson(),\n currencyCode: this._currencyCode,\n };\n }\n\n /**\n * Legacy method for backwards compatibility - converts to JSON string\n * @deprecated Use JSON.stringify(entry.toJson()) instead\n */\n toJSON(): string {\n return JSON.stringify(this.toJson());\n }\n\n /**\n * Legacy method for backwards compatibility\n * @deprecated Use fromJson() instead\n */\n static fromJSON(jsonString: string): WalletStatementEntry | undefined {\n return WalletStatementEntry.fromJson(jsonString);\n }\n\n /**\n * Legacy method for backwards compatibility - creates from plain object\n * @deprecated Use fromJson() for serialized data or create() for raw data\n */\n static from(data: any): WalletStatementEntry | undefined {\n if (!data || typeof data !== \"object\") {\n console.error(\"Invalid data provided to WalletStatementEntry.from: not an object or null.\");\n return undefined;\n }\n\n try {\n // Handle different data formats\n let amountCredited: number;\n let amountDebited: number;\n let balance: number;\n let currencyCode: string = data.currencyCode || \"TZS\";\n\n // Check if amounts are already AmountJson objects (serialized format)\n if (data.amountCredited && typeof data.amountCredited === \"object\" && \"value\" in data.amountCredited) {\n // Data is in serialized format with AmountJson objects\n const creditedAmount = Amount.fromJson(data.amountCredited);\n const debitedAmount = Amount.fromJson(data.amountDebited);\n const balanceAmount = Amount.fromJson(data.balance);\n\n if (!creditedAmount || !debitedAmount || !balanceAmount) {\n console.error(\"Failed to deserialize Amount objects from JSON\");\n return undefined;\n }\n\n amountCredited = creditedAmount.numericValue;\n amountDebited = debitedAmount.numericValue;\n balance = balanceAmount.numericValue;\n\n // Use currency from Amount objects if not provided\n if (!data.currencyCode) {\n currencyCode = creditedAmount.currencyCode;\n }\n } else {\n // Data is in raw format with numeric values\n amountCredited = Number(data.amountCredited);\n amountDebited = Number(data.amountDebited);\n balance = Number(data.balance);\n\n if (!Number.isFinite(amountCredited) || !Number.isFinite(amountDebited) || !Number.isFinite(balance)) {\n console.error(\"Invalid numeric values for amounts\");\n return undefined;\n }\n }\n\n // Handle narration - could be string or Narration object or NarrationJson\n let narration: string | Narration;\n if (typeof data.narration === \"string\") {\n narration = data.narration;\n } else if (Narration.is(data.narration)) {\n narration = data.narration;\n } else if (Narration.isNarrationJson(data.narration)) {\n const narrationInstance = Narration.fromJson(data.narration);\n if (!narrationInstance) {\n console.error(\"Failed to create Narration from NarrationJson\");\n return undefined;\n }\n narration = narrationInstance;\n } else {\n console.error(\"Invalid narration data\");\n return undefined;\n }\n\n // Use the create method which includes validation and date coercion\n return WalletStatementEntry.create({\n accountNo: data.accountNo,\n debitOrCredit: data.debitOrCredit,\n tranRefNo: data.tranRefNo,\n narration,\n txnDate: data.txnDate,\n valueDate: data.valueDate,\n amountCredited,\n amountDebited,\n balance,\n currencyCode,\n });\n } catch (error) {\n console.error(\"Error in WalletStatementEntry.from:\", error);\n return undefined;\n }\n }\n}\n","import { RoleDTO } from \"@/features/admin/admin.dtos\";\n\n// ====================== Role Entity ====================== //\nexport class Role {\n public readonly id: string;\n public readonly name: string;\n public readonly description?: string;\n public readonly permissions: ReadonlySet<string>;\n public readonly createdAt: Date;\n public readonly updatedAt: Date;\n\n constructor(data: RoleDTO) {\n this.id = data.id;\n this.name = data.name;\n this.description = data.description;\n this.permissions = new Set(data.access);\n this.createdAt = new Date(data.createdAt);\n this.updatedAt = new Date(data.updatedAt);\n }\n\n hasPermission(permission: string): boolean {\n return this.permissions.has(permission);\n }\n\n static from(data: any): Role | undefined {\n try {\n if (!data?.id || !data?.name || !Array.isArray(data?.access)) {\n return undefined;\n }\n return new Role(data);\n } catch (error) {\n console.error(\"Error creating Role:\", error);\n return undefined;\n }\n }\n\n toJSON(): any {\n return {\n id: this.id,\n name: this.name,\n description: this.description,\n access: Array.from(this.permissions),\n createdAt: this.createdAt.toISOString(),\n updatedAt: this.updatedAt.toISOString(),\n };\n }\n}\n","// deno-lint-ignore-file no-explicit-any\n// ====================== Base User Entity ====================== //\n\nimport { RoleDTO } from \"@/features/admin/admin.dtos\";\nimport { Role } from \"./role.model\";\n\nexport interface UserEntityData {\n id: string;\n name: string;\n identity: string;\n profileId: string;\n permissions: string[];\n}\n\n/**\n * Base user entity - represents a user in the system\n */\nexport class UserEntity {\n public readonly id: string;\n public readonly name: string;\n public readonly identity: string; // email or phone\n public readonly profileId: string;\n public readonly permissions: ReadonlySet<string>;\n\n constructor(data: UserEntityData) {\n this.id = data.id;\n this.name = data.name;\n this.identity = data.identity;\n this.profileId = data.profileId;\n this.permissions = new Set(data.permissions);\n }\n\n /**\n * Check if user has a specific permission\n */\n can(permission: string): boolean {\n return this.permissions.has(permission);\n }\n\n /**\n * Check if user has any of the specified permissions\n */\n canAny(permissions: string[]): boolean {\n return permissions.some((p) => this.permissions.has(p));\n }\n\n /**\n * Check if user has all of the specified permissions\n */\n canAll(permissions: string[]): boolean {\n return permissions.every((p) => this.permissions.has(p));\n }\n}\n\n// ====================== Authenticated User (Current Session) ====================== //\n\nexport interface AuthenticatedUserData {\n name: string;\n identity: string;\n profileId: string;\n profile: any;\n token: string;\n resetPassword: boolean;\n permissions: string[];\n sessionId?: string;\n expiresAt?: string;\n}\n\n// ====================== Managed User (Admin View) ====================== //\n\nexport interface ManagedUserData {\n id: string;\n name: string;\n identity: string;\n type: string;\n profileId: string;\n roleId: string;\n resetPassword: boolean;\n isActive: boolean;\n isArchived: boolean;\n role?: RoleDTO;\n createdAt: string;\n updatedAt: string;\n}\n\n/**\n * Represents a user from the admin management perspective.\n * Same person as AuthenticatedUser but with management metadata and capabilities.\n */\nexport class ManagedUser extends UserEntity {\n public readonly type: string;\n public readonly roleId: string;\n public readonly resetPassword: boolean;\n public readonly isActive: boolean;\n public readonly isArchived: boolean;\n public readonly role?: Role;\n public readonly createdAt: Date;\n public readonly updatedAt: Date;\n\n constructor(data: ManagedUserData) {\n super({\n id: data.id,\n name: data.name,\n identity: data.identity,\n profileId: data.profileId,\n permissions: data.role?.access ?? [],\n });\n\n this.type = data.type;\n this.roleId = data.roleId;\n this.resetPassword = data.resetPassword;\n this.isActive = data.isActive;\n this.isArchived = data.isArchived;\n this.createdAt = new Date(data.createdAt);\n this.updatedAt = new Date(data.updatedAt);\n\n if (data.role) {\n try {\n this.role = new Role(data.role);\n } catch (_) {\n //\n }\n }\n }\n\n /**\n * Check if user account is active\n */\n isAccountActive(): boolean {\n return this.isActive;\n }\n\n /**\n * Check if user account is archived\n */\n isAccountArchived(): boolean {\n return this.isArchived;\n }\n\n /**\n * Check if user needs to reset password\n */\n needsPasswordReset(): boolean {\n return this.resetPassword;\n }\n\n /**\n * Get comprehensive account status\n */\n getAccountStatus(): {\n status: \"active\" | \"inactive\" | \"archived\" | \"password_reset_required\";\n label: string;\n color: \"success\" | \"warning\" | \"error\" | \"default\";\n description: string;\n } {\n if (this.isArchived) {\n return {\n status: \"archived\",\n label: \"Archived\",\n color: \"default\",\n description: \"Account has been archived and is no longer accessible\",\n };\n }\n\n if (!this.isActive) {\n return {\n status: \"inactive\",\n label: \"Inactive\",\n color: \"error\",\n description: \"Account has been deactivated by an administrator\",\n };\n }\n\n if (this.resetPassword) {\n return {\n status: \"password_reset_required\",\n label: \"Password Reset Required\",\n color: \"warning\",\n description: \"User must reset their password on next login\",\n };\n }\n\n return {\n status: \"active\",\n label: \"Active\",\n color: \"success\",\n description: \"Account is active and ready to use\",\n };\n }\n\n /**\n * Get role display name\n */\n getRoleName(): string {\n return this.role?.name ?? \"\";\n }\n\n /**\n * Get formatted creation date\n */\n getCreatedDate(): string {\n return this.createdAt.toLocaleDateString();\n }\n\n /**\n * Get time since last update\n */\n getLastUpdateInfo(): string {\n const now = new Date();\n const diffMs = now.getTime() - this.updatedAt.getTime();\n const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n\n if (diffDays === 0) return \"Today\";\n if (diffDays === 1) return \"Yesterday\";\n if (diffDays < 7) return `${diffDays} days ago`;\n if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`;\n return this.updatedAt.toLocaleDateString();\n }\n\n static from(data: any): ManagedUser | undefined {\n try {\n if (!data?.id || !data?.name || !data?.identity || !data?.roleId) {\n console.error(\"Missing required ManagedUser fields:\", data);\n return undefined;\n }\n return new ManagedUser(data);\n } catch (error) {\n console.error(\"Error creating ManagedUser:\", error);\n return undefined;\n }\n }\n\n static createMany(dataArray: any[]): ManagedUser[] {\n return dataArray.map((data) => ManagedUser.from(data)).filter(Boolean) as ManagedUser[];\n }\n\n toJSON(): any {\n return {\n id: this.id,\n name: this.name,\n identity: this.identity,\n type: this.type,\n profileId: this.profileId,\n roleId: this.roleId,\n resetPassword: this.resetPassword,\n isActive: this.isActive,\n isArchived: this.isArchived,\n role: this.role?.toJSON(),\n createdAt: this.createdAt.toISOString(),\n updatedAt: this.updatedAt.toISOString(),\n };\n }\n}\n","import { z } from 'zod';\n\nconst isZodType = (obj) => {\n return typeof (obj === null || obj === void 0 ? void 0 : obj.safeParse) === 'function';\n};\nconst isZodObject = (obj) => {\n if (typeof (obj === null || obj === void 0 ? void 0 : obj.passthrough) === 'function') {\n return true;\n }\n if (typeof (obj === null || obj === void 0 ? void 0 : obj.innerType) === 'function') {\n return isZodObject(obj === null || obj === void 0 ? void 0 : obj.innerType());\n }\n return false;\n};\nconst isZodObjectStrict = (obj) => {\n return typeof (obj === null || obj === void 0 ? void 0 : obj.passthrough) === 'function';\n};\nconst extractZodObjectShape = (obj) => {\n if (!isZodObject(obj)) {\n throw new Error('Unknown zod object type');\n }\n if ('innerType' in obj) {\n return extractZodObjectShape(obj.innerType());\n }\n return obj.shape;\n};\nconst zodMerge = (objectA, objectB) => {\n if (isZodObjectStrict(objectA)) {\n if (isZodObjectStrict(objectB)) {\n return objectA.merge(objectB);\n }\n return objectA;\n }\n if (isZodObjectStrict(objectB)) {\n return objectB;\n }\n return Object.assign({}, objectA, objectB);\n};\nconst checkZodSchema = (data, schema, { passThroughExtraKeys = false } = {}) => {\n if (isZodType(schema)) {\n const result = schema.safeParse(data);\n if (result.success) {\n return {\n success: true,\n data: passThroughExtraKeys && typeof data === 'object'\n ? { ...data, ...result.data }\n : result.data,\n };\n }\n return {\n success: false,\n error: result.error,\n };\n }\n return {\n success: true,\n data: data,\n };\n};\n// Convert a ZodError to a plain object because ZodError extends Error and causes problems with NestJS\nconst zodErrorResponse = (error) => {\n return {\n name: error.name,\n issues: error.issues,\n };\n};\nconst ZodErrorSchema = z.object({\n name: z.literal('ZodError'),\n issues: z.array(z\n .object({\n path: z.array(z.union([z.string(), z.number()])),\n message: z.string().optional(),\n code: z.nativeEnum(z.ZodIssueCode),\n })\n // ZodIssuse type are complex and potentially unstable. So we don’t deal with his specific fields other than the common.\n .catchall(z.any())),\n});\n\nconst ContractNoBody = Symbol('ContractNoBody');\n/**\n * Differentiate between a route and a router\n *\n * @param obj\n * @returns\n */\nconst isAppRoute = (obj) => {\n return 'method' in obj && 'path' in obj;\n};\nconst isAppRouteQuery = (route) => {\n return route.method === 'GET';\n};\nconst isAppRouteMutation = (route) => {\n return !isAppRouteQuery(route);\n};\n/**\n *\n * @deprecated Please use {@link initContract} instead.\n */\nconst initTsRest = () => initContract();\nconst recursivelyApplyOptions = (router, options) => {\n return Object.fromEntries(Object.entries(router).map(([key, value]) => {\n var _a, _b, _c;\n if (isAppRoute(value)) {\n return [\n key,\n {\n ...value,\n path: (options === null || options === void 0 ? void 0 : options.pathPrefix)\n ? options.pathPrefix + value.path\n : value.path,\n headers: zodMerge(options === null || options === void 0 ? void 0 : options.baseHeaders, value.headers),\n strictStatusCodes: (_a = value.strictStatusCodes) !== null && _a !== void 0 ? _a : options === null || options === void 0 ? void 0 : options.strictStatusCodes,\n validateResponseOnClient: (_b = value.validateResponseOnClient) !== null && _b !== void 0 ? _b : options === null || options === void 0 ? void 0 : options.validateResponseOnClient,\n responses: {\n ...options === null || options === void 0 ? void 0 : options.commonResponses,\n ...value.responses,\n },\n metadata: (options === null || options === void 0 ? void 0 : options.metadata)\n ? {\n ...options === null || options === void 0 ? void 0 : options.metadata,\n ...((_c = value.metadata) !== null && _c !== void 0 ? _c : {}),\n }\n : value.metadata,\n },\n ];\n }\n else {\n return [key, recursivelyApplyOptions(value, options)];\n }\n }));\n};\nconst ContractPlainTypeRuntimeSymbol = Symbol('ContractPlainType');\n/**\n * Instantiate a ts-rest client, primarily to access `router`, `response`, and `body`\n *\n * @returns {ContractInstance}\n */\nconst initContract = () => {\n return {\n // @ts-expect-error - this is a type error, but it's not clear how to fix it\n router: (endpoints, options) => recursivelyApplyOptions(endpoints, options),\n query: (args) => args,\n mutation: (args) => args,\n responses: (args) => args,\n response: () => ContractPlainTypeRuntimeSymbol,\n body: () => ContractPlainTypeRuntimeSymbol,\n type: () => ContractPlainTypeRuntimeSymbol,\n otherResponse: ({ contentType, body, }) => ({\n contentType,\n body,\n }),\n noBody: () => ContractNoBody,\n };\n};\n\n/**\n * @param path - The URL e.g. /posts/:id\n * @param params - The params e.g. `{ id: string }`\n * @returns - The URL with the params e.g. /posts/123\n */\nconst insertParamsIntoPath = ({ path, params, }) => {\n const pathParams = params;\n return path.replace(/\\/?:([^/?]+)\\??/g, (matched, p) => pathParams[p]\n ? `${matched.startsWith('/') ? '/' : ''}${pathParams[p]}`\n : '');\n};\n\n/**\n *\n * @param query - Any JSON object\n * @param json - Use JSON.stringify to encode the query values\n * @returns - The query url segment, using explode array syntax, and deep object syntax\n */\nconst convertQueryParamsToUrlString = (query, json = false) => {\n const queryString = json\n ? encodeQueryParamsJson(query)\n : encodeQueryParams(query);\n return (queryString === null || queryString === void 0 ? void 0 : queryString.length) > 0 ? '?' + queryString : '';\n};\nconst encodeQueryParamsJson = (query) => {\n if (!query) {\n return '';\n }\n return Object.entries(query)\n .filter(([, value]) => value !== undefined)\n .map(([key, value]) => {\n let encodedValue;\n // if value is a string and is not a reserved JSON value or a number, pass it without encoding\n // this makes strings look nicer in the URL (e.g. ?name=John instead of ?name=%22John%22)\n // this is also how OpenAPI will pass strings even if they are marked as application/json types\n if (typeof value === 'string' &&\n !['true', 'false', 'null'].includes(value.trim()) &&\n isNaN(Number(value))) {\n encodedValue = value;\n }\n else {\n encodedValue = JSON.stringify(value);\n }\n return `${encodeURIComponent(key)}=${encodeURIComponent(encodedValue)}`;\n })\n .join('&');\n};\nconst encodeQueryParams = (query) => {\n if (!query) {\n return '';\n }\n return (Object.keys(query)\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n .flatMap((key) => tokeniseValue(key, query[key]))\n .map(([key, value]) => {\n return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;\n })\n .join('&'));\n};\n/**\n * A recursive function to convert an object/string/number/Date/whatever into an array of key=value pairs\n *\n * The output of this should be flatMap-able to a string of key=value pairs which can be\n * joined with & to form a query string\n *\n * This should be fully compatible with the \"qs\" library, but without the need to add a dependency\n */\nconst tokeniseValue = (key, value) => {\n if (Array.isArray(value)) {\n return value.flatMap((v, idx) => tokeniseValue(`${key}[${idx}]`, v));\n }\n if (value instanceof Date) {\n return [[`${key}`, value.toISOString()]];\n }\n if (value === null) {\n return [[`${key}`, '']];\n }\n if (value === undefined) {\n return [];\n }\n if (typeof value === 'object') {\n return Object.keys(value).flatMap((k) => \n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n tokeniseValue(`${key}[${k}]`, value[k]));\n }\n return [[`${key}`, `${value}`]];\n};\n/**\n *\n * @param query - A server-side query object where values have been encoded as JSON strings\n * @returns - The same object with the JSON strings decoded. Objects that were encoded using toJSON such as Dates will remain as strings\n */\nconst parseJsonQueryObject = (query) => {\n return Object.fromEntries(Object.entries(query).map(([key, value]) => {\n let parsedValue;\n // if json parse fails, treat the value as a string\n // this allows us to pass strings without having to surround them with quotes\n try {\n parsedValue = JSON.parse(value);\n }\n catch {\n parsedValue = value;\n }\n return [key, parsedValue];\n }));\n};\n\nclass UnknownStatusError extends Error {\n constructor(response, knownResponseStatuses) {\n const expectedStatuses = knownResponseStatuses.join(',');\n super(`Server returned unexpected response. Expected one of: ${expectedStatuses} got: ${response.status}`);\n this.response = response;\n }\n}\n\n/**\n * @deprecated Only safe to use on the client-side. Use `ServerInferResponses`/`ClientInferResponses` instead.\n */\nfunction getRouteResponses(router) {\n return {};\n}\n/**\n * Default fetch api implementation:\n *\n * Can be used as a reference for implementing your own fetcher,\n * or used in the \"api\" field of ClientArgs to allow you to hook\n * into the request to run custom logic\n */\nconst tsRestFetchApi = async ({ route, path, method, headers, body, validateResponse, fetchOptions, }) => {\n const result = await fetch(path, {\n ...fetchOptions,\n method,\n headers,\n body,\n });\n const contentType = result.headers.get('content-type');\n if ((contentType === null || contentType === void 0 ? void 0 : contentType.includes('application/')) && (contentType === null || contentType === void 0 ? void 0 : contentType.includes('json'))) {\n const response = {\n status: result.status,\n body: await result.json(),\n headers: result.headers,\n };\n const responseSchema = route.responses[response.status];\n if ((validateResponse !== null && validateResponse !== void 0 ? validateResponse : route.validateResponseOnClient) &&\n isZodType(responseSchema)) {\n return {\n ...response,\n body: responseSchema.parse(response.body),\n };\n }\n return response;\n }\n if (contentType === null || contentType === void 0 ? void 0 : contentType.includes('text/')) {\n return {\n status: result.status,\n body: await result.text(),\n headers: result.headers,\n };\n }\n return {\n status: result.status,\n body: await result.blob(),\n headers: result.headers,\n };\n};\nconst createFormData = (body) => {\n const formData = new FormData();\n const appendToFormData = (key, value) => {\n if (value instanceof File) {\n formData.append(key, value);\n }\n else {\n formData.append(key, JSON.stringify(value));\n }\n };\n Object.entries(body).forEach(([key, value]) => {\n if (Array.isArray(value)) {\n for (const item of value) {\n appendToFormData(key, item);\n }\n }\n else {\n appendToFormData(key, value);\n }\n });\n return formData;\n};\nconst normalizeHeaders = (headers) => {\n return Object.fromEntries(Object.entries(headers).map(([k, v]) => [k.toLowerCase(), v]));\n};\nconst fetchApi = (options) => {\n const { path, clientArgs, route, body, query, extraInputArgs, headers, fetchOptions, } = options;\n const apiFetcher = clientArgs.api || tsRestFetchApi;\n const baseHeaders = clientArgs.baseHeaders &&\n Object.fromEntries(Object.entries(clientArgs.baseHeaders).map(([name, valueOrFunction]) => {\n if (typeof valueOrFunction === 'function') {\n return [name, valueOrFunction(options)];\n }\n else {\n return [name, valueOrFunction];\n }\n }));\n const combinedHeaders = {\n ...(baseHeaders && normalizeHeaders(baseHeaders)),\n ...normalizeHeaders(headers),\n };\n // Remove any headers that are set to undefined\n Object.keys(combinedHeaders).forEach((key) => {\n if (combinedHeaders[key] === undefined) {\n delete combinedHeaders[key];\n }\n });\n let fetcherArgs = {\n route,\n path,\n method: route.method,\n headers: combinedHeaders,\n body: undefined,\n rawBody: body,\n rawQuery: query,\n contentType: undefined,\n validateResponse: clientArgs.validateResponse,\n fetchOptions: {\n ...(clientArgs.credentials && { credentials: clientArgs.credentials }),\n ...fetchOptions,\n },\n ...((fetchOptions === null || fetchOptions === void 0 ? void 0 : fetchOptions.signal) && { signal: fetchOptions.signal }),\n ...((fetchOptions === null || fetchOptions === void 0 ? void 0 : fetchOptions.cache) && { cache: fetchOptions.cache }),\n ...(fetchOptions &&\n 'next' in fetchOptions &&\n !!(fetchOptions === null || fetchOptions === void 0 ? void 0 : fetchOptions.next) && { next: fetchOptions.next }),\n };\n if (route.method !== 'GET') {\n if ('contentType' in route && route.contentType === 'multipart/form-data') {\n fetcherArgs = {\n ...fetcherArgs,\n contentType: 'multipart/form-data',\n body: body instanceof FormData ? body : createFormData(body),\n };\n }\n else if ('contentType' in route &&\n route.contentType === 'application/x-www-form-urlencoded') {\n fetcherArgs = {\n ...fetcherArgs,\n contentType: 'application/x-www-form-urlencoded',\n headers: {\n 'content-type': 'application/x-www-form-urlencoded',\n ...fetcherArgs.headers,\n },\n body: typeof body === 'string'\n ? body\n : new URLSearchParams(body),\n };\n }\n else if (body !== null && body !== undefined) {\n fetcherArgs = {\n ...fetcherArgs,\n contentType: 'application/json',\n headers: {\n 'content-type': 'application/json',\n ...fetcherArgs.headers,\n },\n body: JSON.stringify(body),\n };\n }\n }\n return apiFetcher({\n ...fetcherArgs,\n ...extraInputArgs,\n });\n};\nconst evaluateFetchApiArgs = (route, clientArgs, inputArgs) => {\n const { query, params, body, headers, extraHeaders, overrideClientOptions, fetchOptions, \n // TODO: remove in 4.0\n cache, \n // TODO: remove in 4.0\n next, \n // extra input args\n ...extraInputArgs } = inputArgs || {};\n const overriddenClientArgs = {\n ...clientArgs,\n ...overrideClientOptions,\n };\n const completeUrl = getCompleteUrl(query, overriddenClientArgs.baseUrl, params, route, !!overriddenClientArgs.jsonQuery);\n return {\n path: completeUrl,\n clientArgs: overriddenClientArgs,\n route,\n body,\n query,\n extraInputArgs,\n fetchOptions: {\n ...(cache && { cache }),\n ...(next && { next }),\n ...fetchOptions,\n },\n headers: {\n ...extraHeaders,\n ...headers,\n },\n };\n};\n/**\n * @hidden\n */\nconst getCompleteUrl = (query, baseUrl, params, route, jsonQuery) => {\n const path = insertParamsIntoPath({\n path: route.path,\n params: params,\n });\n const queryComponent = convertQueryParamsToUrlString(query, jsonQuery);\n return `${baseUrl}${path}${queryComponent}`;\n};\nconst getRouteQuery = (route, clientArgs) => {\n const knownResponseStatuses = Object.keys(route.responses);\n return async (inputArgs) => {\n const fetchApiArgs = evaluateFetchApiArgs(route, clientArgs, inputArgs);\n const response = await fetchApi(fetchApiArgs);\n // TODO: in next major version, throw by default if `strictStatusCode` is enabled\n if (!clientArgs.throwOnUnknownStatus) {\n return response;\n }\n if (knownResponseStatuses.includes(response.status.toString())) {\n return response;\n }\n throw new UnknownStatusError(response, knownResponseStatuses);\n };\n};\nconst initClient = (router, args) => {\n return Object.fromEntries(Object.entries(router).map(([key, subRouter]) => {\n if (isAppRoute(subRouter)) {\n return [key, getRouteQuery(subRouter, args)];\n }\n else {\n return [key, initClient(subRouter, args)];\n }\n }));\n};\n\nclass ResponseValidationError extends Error {\n constructor(appRoute, cause) {\n super(`[ts-rest] Response validation failed for ${appRoute.method} ${appRoute.path}: ${cause.message}`);\n this.appRoute = appRoute;\n this.cause = cause;\n }\n}\n\nconst isAppRouteResponse = (value) => {\n return (value != null &&\n typeof value === 'object' &&\n 'status' in value &&\n typeof value.status === 'number');\n};\nconst isAppRouteOtherResponse = (response) => {\n return (response != null &&\n typeof response === 'object' &&\n 'contentType' in response);\n};\nconst isAppRouteNoBody = (response) => {\n return response === ContractNoBody;\n};\nconst validateResponse = ({ appRoute, response, }) => {\n if (isAppRouteResponse(response)) {\n const responseType = appRoute.responses[response.status];\n const responseSchema = isAppRouteOtherResponse(responseType)\n ? responseType.body\n : responseType;\n const responseValidation = checkZodSchema(response.body, responseSchema);\n if (!responseValidation.success) {\n throw new ResponseValidationError(appRoute, responseValidation.error);\n }\n return {\n status: response.status,\n body: responseValidation.data,\n };\n }\n return response;\n};\n\nclass TsRestResponseError extends Error {\n constructor(route, response) {\n super();\n this.statusCode = response.status;\n this.body = response.body;\n this.name = this.constructor.name;\n if (typeof response.body === 'string') {\n this.message = response.body;\n }\n else if (typeof response.body === 'object' &&\n response.body !== null &&\n 'message' in response.body &&\n typeof response.body.message === 'string') {\n this.message = response.body['message'];\n }\n else {\n this.message = 'Error';\n }\n }\n}\n\nconst isResponse = (response, contractEndpoint) => {\n return (typeof response === 'object' &&\n response !== null &&\n 'status' in response &&\n 'body' in response &&\n typeof response.status === 'number' &&\n response.status >= 200 &&\n response.status < 600 &&\n ((contractEndpoint === null || contractEndpoint === void 0 ? void 0 : contractEndpoint.strictStatusCodes)\n ? Object.keys(contractEndpoint.responses).includes(response.status.toString())\n : true));\n};\nconst isSuccessResponse = (response, contractEndpoint) => {\n return (isResponse(response, contractEndpoint) &&\n response.status >= 200 &&\n response.status < 300);\n};\nconst isErrorResponse = (response, contractEndpoint) => {\n return (isResponse(response, contractEndpoint) &&\n !isSuccessResponse(response, contractEndpoint));\n};\nconst isUnknownResponse = (response, contractEndpoint) => {\n return (isResponse(response) &&\n !Object.keys(contractEndpoint.responses).includes(response.status.toString()));\n};\nconst isUnknownSuccessResponse = (response, contractEndpoint) => {\n return (isSuccessResponse(response) && isUnknownResponse(response, contractEndpoint));\n};\nconst isUnknownErrorResponse = (response, contractEndpoint) => {\n return (isErrorResponse(response) && isUnknownResponse(response, contractEndpoint));\n};\nconst exhaustiveGuard = (response) => {\n throw new Error(`Unreachable code: Response status is ${response.status}`);\n};\n\nexport { ContractNoBody, ContractPlainTypeRuntimeSymbol, ResponseValidationError, TsRestResponseError, UnknownStatusError, ZodErrorSchema, checkZodSchema, convertQueryParamsToUrlString, encodeQueryParams, encodeQueryParamsJson, evaluateFetchApiArgs, exhaustiveGuard, extractZodObjectShape, fetchApi, getCompleteUrl, getRouteQuery, getRouteResponses, initClient, initContract, initTsRest, insertParamsIntoPath, isAppRoute, isAppRouteMutation, isAppRouteNoBody, isAppRouteOtherResponse, isAppRouteQuery, isAppRouteResponse, isErrorResponse, isResponse, isSuccessResponse, isUnknownErrorResponse, isUnknownResponse, isUnknownSuccessResponse, isZodObject, isZodObjectStrict, isZodType, parseJsonQueryObject, tsRestFetchApi, validateResponse, zodErrorResponse, zodMerge };\n","const reduxImpl = (reducer, initial) => (set, _get, api) => {\n api.dispatch = (action) => {\n set((state) => reducer(state, action), false, action);\n return action;\n };\n api.dispatchFromDevtools = true;\n return { dispatch: (...a) => api.dispatch(...a), ...initial };\n};\nconst redux = reduxImpl;\n\nconst trackedConnections = /* @__PURE__ */ new Map();\nconst getTrackedConnectionState = (name) => {\n const api = trackedConnections.get(name);\n if (!api) return {};\n return Object.fromEntries(\n Object.entries(api.stores).map(([key, api2]) => [key, api2.getState()])\n );\n};\nconst extractConnectionInformation = (store, extensionConnector, options) => {\n if (store === void 0) {\n return {\n type: \"untracked\",\n connection: extensionConnector.connect(options)\n };\n }\n const existingConnection = trackedConnections.get(options.name);\n if (existingConnection) {\n return { type: \"tracked\", store, ...existingConnection };\n }\n const newConnection = {\n connection: extensionConnector.connect(options),\n stores: {}\n };\n trackedConnections.set(options.name, newConnection);\n return { type: \"tracked\", store, ...newConnection };\n};\nconst devtoolsImpl = (fn, devtoolsOptions = {}) => (set, get, api) => {\n const { enabled, anonymousActionType, store, ...options } = devtoolsOptions;\n let extensionConnector;\n try {\n extensionConnector = (enabled != null ? enabled : (import.meta.env ? import.meta.env.MODE : void 0) !== \"production\") && window.__REDUX_DEVTOOLS_EXTENSION__;\n } catch (_e) {\n }\n if (!extensionConnector) {\n if ((import.meta.env ? import.meta.env.MODE : void 0) !== \"production\" && enabled) {\n console.warn(\n \"[zustand devtools middleware] Please install/enable Redux devtools extension\"\n );\n }\n return fn(set, get, api);\n }\n const { connection, ...connectionInformation } = extractConnectionInformation(store, extensionConnector, options);\n let isRecording = true;\n api.setState = (state, replace, nameOrAction) => {\n const r = set(state, replace);\n if (!isRecording) return r;\n const action = nameOrAction === void 0 ? { type: anonymousActionType || \"anonymous\" } : typeof nameOrAction === \"string\" ? { type: nameOrAction } : nameOrAction;\n if (store === void 0) {\n connection == null ? void 0 : connection.send(action, get());\n return r;\n }\n connection == null ? void 0 : connection.send(\n {\n ...action,\n type: `${store}/${action.type}`\n },\n {\n ...getTrackedConnectionState(options.name),\n [store]: api.getState()\n }\n );\n return r;\n };\n const setStateFromDevtools = (...a) => {\n const originalIsRecording = isRecording;\n isRecording = false;\n set(...a);\n isRecording = originalIsRecording;\n };\n const initialState = fn(api.setState, get, api);\n if (connectionInformation.type === \"untracked\") {\n connection == null ? void 0 : connection.init(initialState);\n } else {\n connectionInformation.stores[connectionInformation.store] = api;\n connection == null ? void 0 : connection.init(\n Object.fromEntries(\n Object.entries(connectionInformation.stores).map(([key, store2]) => [\n key,\n key === connectionInformation.store ? initialState : store2.getState()\n ])\n )\n );\n }\n if (api.dispatchFromDevtools && typeof api.dispatch === \"function\") {\n let didWarnAboutReservedActionType = false;\n const originalDispatch = api.dispatch;\n api.dispatch = (...a) => {\n if ((import.meta.env ? import.meta.env.MODE : void 0) !== \"production\" && a[0].type === \"__setState\" && !didWarnAboutReservedActionType) {\n console.warn(\n '[zustand devtools middleware] \"__setState\" action type is reserved to set state from the devtools. Avoid using it.'\n );\n didWarnAboutReservedActionType = true;\n }\n originalDispatch(...a);\n };\n }\n connection.subscribe((message) => {\n var _a;\n switch (message.type) {\n case \"ACTION\":\n if (typeof message.payload !== \"string\") {\n console.error(\n \"[zustand devtools middleware] Unsupported action format\"\n );\n return;\n }\n return parseJsonThen(\n message.payload,\n (action) => {\n if (action.type === \"__setState\") {\n if (store === void 0) {\n setStateFromDevtools(action.state);\n return;\n }\n if (Object.keys(action.state).length !== 1) {\n console.error(\n `\n [zustand devtools middleware] Unsupported __setState action format. \n When using 'store' option in devtools(), the 'state' should have only one key, which is a value of 'store' that was passed in devtools(),\n and value of this only key should be a state object. Example: { \"type\": \"__setState\", \"state\": { \"abc123Store\": { \"foo\": \"bar\" } } }\n `\n );\n }\n const stateFromDevtools = action.state[store];\n if (stateFromDevtools === void 0 || stateFromDevtools === null) {\n return;\n }\n if (JSON.stringify(api.getState()) !== JSON.stringify(stateFromDevtools)) {\n setStateFromDevtools(stateFromDevtools);\n }\n return;\n }\n if (!api.dispatchFromDevtools) return;\n if (typeof api.dispatch !== \"function\") return;\n api.dispatch(action);\n }\n );\n case \"DISPATCH\":\n switch (message.payload.type) {\n case \"RESET\":\n setStateFromDevtools(initialState);\n if (store === void 0) {\n return connection == null ? void 0 : connection.init(api.getState());\n }\n return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name));\n case \"COMMIT\":\n if (store === void 0) {\n connection == null ? void 0 : connection.init(api.getState());\n return;\n }\n return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name));\n case \"ROLLBACK\":\n return parseJsonThen(message.state, (state) => {\n if (store === void 0) {\n setStateFromDevtools(state);\n connection == null ? void 0 : connection.init(api.getState());\n return;\n }\n setStateFromDevtools(state[store]);\n connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name));\n });\n case \"JUMP_TO_STATE\":\n case \"JUMP_TO_ACTION\":\n return parseJsonThen(message.state, (state) => {\n if (store === void 0) {\n setStateFromDevtools(state);\n return;\n }\n if (JSON.stringify(api.getState()) !== JSON.stringify(state[store])) {\n setStateFromDevtools(state[store]);\n }\n });\n case \"IMPORT_STATE\": {\n const { nextLiftedState } = message.payload;\n const lastComputedState = (_a = nextLiftedState.computedStates.slice(-1)[0]) == null ? void 0 : _a.state;\n if (!lastComputedState) return;\n if (store === void 0) {\n setStateFromDevtools(lastComputedState);\n } else {\n setStateFromDevtools(lastComputedState[store]);\n }\n connection == null ? void 0 : connection.send(\n null,\n // FIXME no-any\n nextLiftedState\n );\n return;\n }\n case \"PAUSE_RECORDING\":\n return isRecording = !isRecording;\n }\n return;\n }\n });\n return initialState;\n};\nconst devtools = devtoolsImpl;\nconst parseJsonThen = (stringified, f) => {\n let parsed;\n try {\n parsed = JSON.parse(stringified);\n } catch (e) {\n console.error(\n \"[zustand devtools middleware] Could not parse the received json\",\n e\n );\n }\n if (parsed !== void 0) f(parsed);\n};\n\nconst subscribeWithSelectorImpl = (fn) => (set, get, api) => {\n const origSubscribe = api.subscribe;\n api.subscribe = (selector, optListener, options) => {\n let listener = selector;\n if (optListener) {\n const equalityFn = (options == null ? void 0 : options.equalityFn) || Object.is;\n let currentSlice = selector(api.getState());\n listener = (state) => {\n const nextSlice = selector(state);\n if (!equalityFn(currentSlice, nextSlice)) {\n const previousSlice = currentSlice;\n optListener(currentSlice = nextSlice, previousSlice);\n }\n };\n if (options == null ? void 0 : options.fireImmediately) {\n optListener(currentSlice, currentSlice);\n }\n }\n return origSubscribe(listener);\n };\n const initialState = fn(set, get, api);\n return initialState;\n};\nconst subscribeWithSelector = subscribeWithSelectorImpl;\n\nconst combine = (initialState, create) => (...a) => Object.assign({}, initialState, create(...a));\n\nfunction createJSONStorage(getStorage, options) {\n let storage;\n try {\n storage = getStorage();\n } catch (_e) {\n return;\n }\n const persistStorage = {\n getItem: (name) => {\n var _a;\n const parse = (str2) => {\n if (str2 === null) {\n return null;\n }\n return JSON.parse(str2, options == null ? void 0 : options.reviver);\n };\n const str = (_a = storage.getItem(name)) != null ? _a : null;\n if (str instanceof Promise) {\n return str.then(parse);\n }\n return parse(str);\n },\n setItem: (name, newValue) => storage.setItem(\n name,\n JSON.stringify(newValue, options == null ? void 0 : options.replacer)\n ),\n removeItem: (name) => storage.removeItem(name)\n };\n return persistStorage;\n}\nconst toThenable = (fn) => (input) => {\n try {\n const result = fn(input);\n if (result instanceof Promise) {\n return result;\n }\n return {\n then(onFulfilled) {\n return toThenable(onFulfilled)(result);\n },\n catch(_onRejected) {\n return this;\n }\n };\n } catch (e) {\n return {\n then(_onFulfilled) {\n return this;\n },\n catch(onRejected) {\n return toThenable(onRejected)(e);\n }\n };\n }\n};\nconst oldImpl = (config, baseOptions) => (set, get, api) => {\n let options = {\n getStorage: () => localStorage,\n serialize: JSON.stringify,\n deserialize: JSON.parse,\n partialize: (state) => state,\n version: 0,\n merge: (persistedState, currentState) => ({\n ...currentState,\n ...persistedState\n }),\n ...baseOptions\n };\n let hasHydrated = false;\n const hydrationListeners = /* @__PURE__ */ new Set();\n const finishHydrationListeners = /* @__PURE__ */ new Set();\n let storage;\n try {\n storage = options.getStorage();\n } catch (_e) {\n }\n if (!storage) {\n return config(\n (...args) => {\n console.warn(\n `[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.`\n );\n set(...args);\n },\n get,\n api\n );\n }\n const thenableSerialize = toThenable(options.serialize);\n const setItem = () => {\n const state = options.partialize({ ...get() });\n let errorInSync;\n const thenable = thenableSerialize({ state, version: options.version }).then(\n (serializedValue) => storage.setItem(options.name, serializedValue)\n ).catch((e) => {\n errorInSync = e;\n });\n if (errorInSync) {\n throw errorInSync;\n }\n return thenable;\n };\n const savedSetState = api.setState;\n api.setState = (state, replace) => {\n savedSetState(state, replace);\n void setItem();\n };\n const configResult = config(\n (...args) => {\n set(...args);\n void setItem();\n },\n get,\n api\n );\n let stateFromStorage;\n const hydrate = () => {\n var _a;\n if (!storage) return;\n hasHydrated = false;\n hydrationListeners.forEach((cb) => cb(get()));\n const postRehydrationCallback = ((_a = options.onRehydrateStorage) == null ? void 0 : _a.call(options, get())) || void 0;\n return toThenable(storage.getItem.bind(storage))(options.name).then((storageValue) => {\n if (storageValue) {\n return options.deserialize(storageValue);\n }\n }).then((deserializedStorageValue) => {\n if (deserializedStorageValue) {\n if (typeof deserializedStorageValue.version === \"number\" && deserializedStorageValue.version !== options.version) {\n if (options.migrate) {\n return options.migrate(\n deserializedStorageValue.state,\n deserializedStorageValue.version\n );\n }\n console.error(\n `State loaded from storage couldn't be migrated since no migrate function was provided`\n );\n } else {\n return deserializedStorageValue.state;\n }\n }\n }).then((migratedState) => {\n var _a2;\n stateFromStorage = options.merge(\n migratedState,\n (_a2 = get()) != null ? _a2 : configResult\n );\n set(stateFromStorage, true);\n return setItem();\n }).then(() => {\n postRehydrationCallback == null ? void 0 : postRehydrationCallback(stateFromStorage, void 0);\n hasHydrated = true;\n finishHydrationListeners.forEach((cb) => cb(stateFromStorage));\n }).catch((e) => {\n postRehydrationCallback == null ? void 0 : postRehydrationCallback(void 0, e);\n });\n };\n api.persist = {\n setOptions: (newOptions) => {\n options = {\n ...options,\n ...newOptions\n };\n if (newOptions.getStorage) {\n storage = newOptions.getStorage();\n }\n },\n clearStorage: () => {\n storage == null ? void 0 : storage.removeItem(options.name);\n },\n getOptions: () => options,\n rehydrate: () => hydrate(),\n hasHydrated: () => hasHydrated,\n onHydrate: (cb) => {\n hydrationListeners.add(cb);\n return () => {\n hydrationListeners.delete(cb);\n };\n },\n onFinishHydration: (cb) => {\n finishHydrationListeners.add(cb);\n return () => {\n finishHydrationListeners.delete(cb);\n };\n }\n };\n hydrate();\n return stateFromStorage || configResult;\n};\nconst newImpl = (config, baseOptions) => (set, get, api) => {\n let options = {\n storage: createJSONStorage(() => localStorage),\n partialize: (state) => state,\n version: 0,\n merge: (persistedState, currentState) => ({\n ...currentState,\n ...persistedState\n }),\n ...baseOptions\n };\n let hasHydrated = false;\n const hydrationListeners = /* @__PURE__ */ new Set();\n const finishHydrationListeners = /* @__PURE__ */ new Set();\n let storage = options.storage;\n if (!storage) {\n return config(\n (...args) => {\n console.warn(\n `[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.`\n );\n set(...args);\n },\n get,\n api\n );\n }\n const setItem = () => {\n const state = options.partialize({ ...get() });\n return storage.setItem(options.name, {\n state,\n version: options.version\n });\n };\n const savedSetState = api.setState;\n api.setState = (state, replace) => {\n savedSetState(state, replace);\n void setItem();\n };\n const configResult = config(\n (...args) => {\n set(...args);\n void setItem();\n },\n get,\n api\n );\n api.getInitialState = () => configResult;\n let stateFromStorage;\n const hydrate = () => {\n var _a, _b;\n if (!storage) return;\n hasHydrated = false;\n hydrationListeners.forEach((cb) => {\n var _a2;\n return cb((_a2 = get()) != null ? _a2 : configResult);\n });\n const postRehydrationCallback = ((_b = options.onRehydrateStorage) == null ? void 0 : _b.call(options, (_a = get()) != null ? _a : configResult)) || void 0;\n return toThenable(storage.getItem.bind(storage))(options.name).then((deserializedStorageValue) => {\n if (deserializedStorageValue) {\n if (typeof deserializedStorageValue.version === \"number\" && deserializedStorageValue.version !== options.version) {\n if (options.migrate) {\n return [\n true,\n options.migrate(\n deserializedStorageValue.state,\n deserializedStorageValue.version\n )\n ];\n }\n console.error(\n `State loaded from storage couldn't be migrated since no migrate function was provided`\n );\n } else {\n return [false, deserializedStorageValue.state];\n }\n }\n return [false, void 0];\n }).then((migrationResult) => {\n var _a2;\n const [migrated, migratedState] = migrationResult;\n stateFromStorage = options.merge(\n migratedState,\n (_a2 = get()) != null ? _a2 : configResult\n );\n set(stateFromStorage, true);\n if (migrated) {\n return setItem();\n }\n }).then(() => {\n postRehydrationCallback == null ? void 0 : postRehydrationCallback(stateFromStorage, void 0);\n stateFromStorage = get();\n hasHydrated = true;\n finishHydrationListeners.forEach((cb) => cb(stateFromStorage));\n }).catch((e) => {\n postRehydrationCallback == null ? void 0 : postRehydrationCallback(void 0, e);\n });\n };\n api.persist = {\n setOptions: (newOptions) => {\n options = {\n ...options,\n ...newOptions\n };\n if (newOptions.storage) {\n storage = newOptions.storage;\n }\n },\n clearStorage: () => {\n storage == null ? void 0 : storage.removeItem(options.name);\n },\n getOptions: () => options,\n rehydrate: () => hydrate(),\n hasHydrated: () => hasHydrated,\n onHydrate: (cb) => {\n hydrationListeners.add(cb);\n return () => {\n hydrationListeners.delete(cb);\n };\n },\n onFinishHydration: (cb) => {\n finishHydrationListeners.add(cb);\n return () => {\n finishHydrationListeners.delete(cb);\n };\n }\n };\n if (!options.skipHydration) {\n hydrate();\n }\n return stateFromStorage || configResult;\n};\nconst persistImpl = (config, baseOptions) => {\n if (\"getStorage\" in baseOptions || \"serialize\" in baseOptions || \"deserialize\" in baseOptions) {\n if ((import.meta.env ? import.meta.env.MODE : void 0) !== \"production\") {\n console.warn(\n \"[DEPRECATED] `getStorage`, `serialize` and `deserialize` options are deprecated. Use `storage` option instead.\"\n );\n }\n return oldImpl(config, baseOptions);\n }\n return newImpl(config, baseOptions);\n};\nconst persist = persistImpl;\n\nexport { combine, createJSONStorage, devtools, persist, redux, subscribeWithSelector };\n","import { create, type StoreApi, type UseBoundStore, useStore } from \"zustand\";\nimport { createJSONStorage, persist } from \"zustand/middleware\";\nimport { User } from \"@/models/user.model\";\n\n/** Key used for storing auth data in session storage */\nconst AUTH_STORE_SESSION_STORAGE_KEY = \"auth-store\";\n\n/** Type definition for the store's internal state */\ntype AuthState = {\n user: string | undefined;\n token: string | undefined;\n};\n\n/** Type definition for the store's actions */\ninterface AuthActions {\n setUser: (user: User) => void;\n getUser: () => User | undefined;\n setToken: (token: string) => void;\n getToken: () => string | undefined;\n setUserAndToken: (user: User, token: string) => void;\n clearAuth: () => void;\n}\n\n// ==============================================================================\n// UNIFIED ZUSTAND STORE\n// ==============================================================================\n\n/**\n * Unified Zustand store for managing both user and token state with persistence.\n *\n * Features:\n * - Persists both user data and token to sessionStorage\n * - Serializes/deserializes User objects to/from JSON\n * - Provides reactive state updates for React components\n * - Automatically syncs user and token state\n * - Server-side rendering safe\n */\nexport const authStore: UseBoundStore<StoreApi<AuthState & AuthActions>> = create<\n AuthState & AuthActions,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any\n>(\n persist(\n (set, get) => ({\n user: undefined,\n token: undefined,\n\n /**\n * Retrieves the current user from the store.\n * Deserializes the JSON string back to a User object.\n *\n * @returns The current user or undefined if not set or invalid\n */\n getUser: () => {\n try {\n const jsonUser = get().user;\n if (jsonUser) {\n return User.fromJSON(jsonUser);\n }\n } catch (error) {\n console.warn(\"Failed to deserialize user from store:\", error);\n }\n return undefined;\n },\n\n /**\n * Sets the current user in the store.\n * Serializes the User object to JSON for storage.\n *\n * @param user - The user to store\n */\n setUser: (user) => {\n try {\n set({ user: user.toJSON() });\n } catch (error) {\n console.error(\"Failed to serialize user to store:\", error);\n }\n },\n\n /**\n * Retrieves the current token from the store.\n *\n * @returns The stored token or undefined if not present\n */\n getToken: () => get().token,\n\n /**\n * Sets the current token in the store.\n *\n * @param token - The token to store\n */\n setToken: (token) => set({ token }),\n\n /**\n * Sets both user and token atomically.\n * This ensures they're always in sync.\n *\n * @param user - The user to store\n * @param token - The token to store\n */\n setUserAndToken: (user, token) => {\n try {\n set({\n user: user.toJSON(),\n token,\n });\n } catch (error) {\n console.error(\"Failed to serialize user and token to store:\", error);\n }\n },\n\n /**\n * Clears both user and token from the store.\n */\n clearAuth: () => set({ user: undefined, token: undefined }),\n }),\n {\n name: AUTH_STORE_SESSION_STORAGE_KEY,\n storage: createJSONStorage(() => {\n if (typeof window === \"undefined\") {\n // Return a mock storage for server-side rendering\n return {\n getItem: () => null,\n setItem: () => {},\n removeItem: () => {},\n };\n }\n return sessionStorage;\n }),\n }\n )\n);\n\n// ==============================================================================\n// GLOBAL ACCESS FUNCTIONS\n// ==============================================================================\n\n/**\n * Gets the current authenticated user from the auth store.\n * This function can be called from anywhere in the application.\n *\n * @returns The current user or undefined if not authenticated\n *\n * @example\n * ```typescript\n * // In BaseRepository or any other file\n * const user = getCurrentUser();\n * if (user) {\n * console.log(`Current user: ${user.name}`);\n * }\n * ```\n */\nexport const getCurrentUser = (): User | undefined => {\n if (typeof window === \"undefined\") {\n return undefined; // Server-side safe\n }\n return authStore.getState().getUser();\n};\n\n/**\n * Gets the current authentication token from the auth store.\n * This function can be called from anywhere in the application,\n * including BaseRepository for automatic token injection.\n *\n * @returns The current token or undefined if not authenticated\n *\n * @example\n * ```typescript\n * // In BaseRepository\n * const token = getCurrentToken();\n * if (token) {\n * // Use token for API calls\n * return initClient(contract, { baseHeaders: { token } });\n * }\n * ```\n */\nexport const getCurrentToken = (): string | undefined => {\n if (typeof window === \"undefined\") {\n return undefined; // Server-side safe\n }\n return authStore.getState().getToken();\n};\n\n/**\n * React hook to access the current user with reactive updates.\n *\n * @returns The current user or undefined if not authenticated\n * @throws {Error} If called in a server environment\n *\n * @example\n * ```typescript\n * function UserProfile() {\n * const user = useCurrentUser();\n *\n * if (!user) {\n * return <div>Please log in</div>;\n * }\n *\n * return <div>Welcome, {user.name}!</div>;\n * }\n * ```\n */\nexport const useCurrentUser = (): User | undefined => {\n if (typeof window === \"undefined\") {\n throw new Error(\"useCurrentUser must only be used in a React component on the client side\");\n }\n\n const jsonUser = useStore(authStore, (state) => state.user);\n\n try {\n return jsonUser ? User.fromJSON(jsonUser) : undefined;\n } catch (error) {\n console.warn(\"Failed to deserialize user from hook:\", error);\n return undefined;\n }\n};\n\n/**\n * React hook to access the current token with reactive updates.\n *\n * @returns The current token or undefined if not authenticated\n * @throws {Error} If called in a server environment\n *\n * @example\n * ```typescript\n * function SomeComponent() {\n * const token = useCurrentToken();\n *\n * if (!token) {\n * return <div>Please log in</div>;\n * }\n *\n * // Use token...\n * }\n * ```\n */\nexport const useCurrentToken = (): string | undefined => {\n if (typeof window === \"undefined\") {\n throw new Error(\"useCurrentToken must only be used in a React component on the client side\");\n }\n\n return useStore(authStore, (state) => state.token);\n};\n\n// ==============================================================================\n// UTILITY FUNCTIONS\n// ==============================================================================\n\n/**\n * Checks if a user is currently authenticated.\n *\n * @returns True if user is authenticated, false otherwise\n *\n * @example\n * ```typescript\n * if (isAuthenticated()) {\n * // User is logged in\n * showDashboard();\n * } else {\n * // User needs to log in\n * showLoginForm();\n * }\n * ```\n */\nexport const isAuthenticated = (): boolean => {\n return !!getCurrentUser() && !!getCurrentToken();\n};\n\n/**\n * Gets debug information about the current authentication state.\n * Useful for troubleshooting authentication issues.\n *\n * @returns Object containing authentication state information\n *\n * @example\n * ```typescript\n * const debugInfo = getAuthDebugInfo();\n * console.log(\"Auth Debug Info:\", debugInfo);\n * ```\n */\nexport const getAuthDebugInfo = () => {\n const user = getCurrentUser();\n const token = getCurrentToken();\n\n return {\n hasUser: !!user,\n hasToken: !!token,\n isAuthenticated: isAuthenticated(),\n userName: user?.name || \"Not logged in\",\n tokenLength: token?.length || 0,\n storeState: typeof window !== \"undefined\" ? authStore.getState() : \"Server-side\",\n };\n};\n","import { AppRouter, initClient } from \"@ts-rest/core\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport { InitClientArgs } from \"@ts-rest/core\";\nimport { Common400APIResponse } from \"./common-responses\";\nimport { APIError } from \"../error\";\nimport { getCurrentToken } from \"@/features/auth/auth.store\";\n\n/**\n * BaseRepository - Generic base class for API repositories\n *\n * Provides a consistent way to interact with the Afloat API using ts-rest contracts.\n * Handles authentication token management through multiple fallback mechanisms.\n *\n * **Token Resolution Order:**\n * 1. Direct token provided in constructor\n * 2. Global authentication state via getCurrentToken()\n *\n * @template TContract - The API contract extending `AppRouter` from `@ts-rest/core`\n *\n * @example\n * ```typescript\n * // Usage 1: Direct token usage (Recommended for server-side usage)\n * const repo = new WalletRepository(\"wallet\", walletContract, {\n * token: \"user-auth-token\"\n * });\n *\n * // Usage 2: Global auth state (Recommended for client-side usage)\n * // Token automatically retrieved from AfloatAuth.instance\n * const repo = new WalletRepository(\"wallet\", walletContract);\n * ```\n */\nexport class BaseRepository<TContract extends AppRouter> {\n /**\n * The ts-rest contract defining the API endpoints\n * @protected\n */\n protected contract: TContract;\n\n /**\n * The API endpoint path for this repository\n * @protected\n */\n protected endpoint: string;\n\n /**\n * Optional custom API root URL\n * @protected\n */\n protected root: string | undefined;\n\n /**\n * Optional authentication token for API calls\n * @protected\n */\n protected token: string | undefined;\n\n /**\n * Creates a new BaseRepository instance.\n *\n * @param endpoint - The API endpoint path (e.g., \"users\", \"wallets\")\n * @param contract - The ts-rest contract defining the API\n * @param options - Optional configuration\n * @param options.root - Custom API root URL (defaults to https://api.afloat.money/v1)\n * @param options.token - Authentication token for API calls\n *\n * @example\n * ```typescript\n * // Server-side with explicit token\n * const repo = new WalletRepository(\"wallet\", walletContract, {\n * token: \"user-auth-token\",\n * root: \"https://api-staging.afloat.money/v1\"\n * });\n *\n * // Client-side using global auth state\n * const repo = new WalletRepository(\"wallet\", walletContract);\n * ```\n */\n constructor(\n endpoint: string,\n contract: TContract,\n options?: {\n root?: string;\n token?: string;\n }\n ) {\n this.contract = contract;\n this.endpoint = endpoint;\n this.root = options?.root;\n this.token = options?.token;\n }\n\n /**\n * Resolves an authentication token for API calls.\n *\n * Uses a fallback chain to find a valid token:\n * 1. Instance token (set in constructor)\n * 2. Global authentication state via getCurrentToken()\n * 3. Return an empty string if no token is found (e.g., for public/unauthenticated endpoints)\n *\n * @protected\n * @returns The authentication token\n * @throws {Error} If no valid token can be found\n *\n * @example\n * ```typescript\n * // This method is called automatically by the client getter\n * // You typically don't need to call this directly\n * ```\n */\n protected getToken(): string {\n // First priority: Direct token provided in constructor\n if (this.token && this.token.trim().length > 0) {\n return this.token;\n }\n\n // Second priority: Global authentication state\n try {\n const globalToken = getCurrentToken();\n if (globalToken && globalToken.trim().length > 0) {\n return globalToken;\n }\n console.warn(`No token available from global auth state for ${this.endpoint} repository`);\n } catch (error) {\n console.warn(\n `No valid token available for ${this.endpoint} repository. ` +\n `Please provide a token directly in the constructor or ensure a user is logged in via AfloatAuth.instance.`\n );\n }\n\n // Priority 3: Return empty string for unauthenticated access\n return \"\";\n }\n\n /**\n * Gets the configured ts-rest client for making API requests.\n *\n * Automatically includes authentication headers and request tracking.\n * The client resolves the authentication token using the fallback chain.\n *\n * @returns Configured ts-rest client instance\n * @throws {Error} If no authentication token is available\n *\n * @example\n * ```typescript\n * // Client automatically includes token and request ID\n * const result = await this.client.getUsers({ query: { limit: 10 } });\n *\n * // Headers automatically included:\n * // - token: \"user-auth-token\"\n * // - x-request-id: \"uuid-v4-string\"\n * ```\n */\n get client() {\n const baseUrl = this.root ? `${this.root}/${this.endpoint}` : `https://api.afloat.money/v1/${this.endpoint}`;\n\n const token = this.getToken();\n\n const clientConfig: InitClientArgs = {\n baseUrl,\n baseHeaders: {\n token: token,\n \"x-request-id\": uuidv4(),\n },\n };\n\n return initClient(this.contract, clientConfig);\n }\n\n /**\n * Updates the authentication token used by this repository.\n *\n * @param token - The new authentication token\n *\n * @example\n * ```typescript\n * // Useful for switching between different user contexts\n * repository.setToken(\"new-auth-token\");\n *\n * // Or clearing the token to fall back to global auth state\n * repository.setToken(undefined);\n * ```\n */\n setToken(token: string): void {\n this.token = token;\n }\n\n /**\n * Gets the currently configured token (if set directly on the instance).\n *\n * @returns The current authentication token or undefined\n *\n * @note This only returns tokens set directly on the instance, not tokens\n * resolved from the global authentication state.\n *\n * @example\n * ```typescript\n * const directToken = repository.getCurrentToken();\n * if (directToken) {\n * console.log(\"Repository has a direct token\");\n * } else {\n * console.log(\"Repository will use global auth state\");\n * }\n * ```\n */\n getCurrentToken(): string | undefined {\n return this.token;\n }\n\n /**\n * Handles API responses consistently across all repositories.\n *\n * Checks the HTTP status code and either returns the response body for\n * successful requests or throws appropriate errors for failures.\n *\n * @template T - The expected type of the successful response body\n * @param result - The API response object with status and body\n * @param successStatusCode - The expected HTTP status code for success\n * @returns The response body typed as T\n * @throws {APIError} For API errors (400, 401, 403, 404, 500, etc.)\n *\n * @example\n * ```typescript\n * const result = await this.client.getUser({ params: { id: \"123\" } });\n * return this.handleResponse<User>(result, 200);\n *\n * // Automatically handles error cases:\n * // - 400: Bad Request with detailed error info\n * // - 401: Unauthorized (authentication required)\n * // - 403: Forbidden (insufficient permissions)\n * // - 404: Not Found\n * // - Other: Generic server error\n * ```\n */\n handleResponse<T>(result: { status: number; body: unknown }, successStatusCode: number): T {\n if (result.status === successStatusCode) {\n return result.body as T;\n }\n\n if (result.status === 400) {\n throw new APIError(result.body as Common400APIResponse);\n }\n\n if (result.status === 401) {\n throw new APIError({\n message: \"You are not authenticated to perform this action. Please login again\",\n statusCode: 401,\n error: \"UNAUTHORIZED\",\n });\n }\n\n if (result.status === 403) {\n throw new APIError({\n message: \"You are not authorized to perform this action.\",\n statusCode: 403,\n error: \"FORBIDDEN\",\n });\n }\n\n if (result.status === 404) {\n throw new APIError({\n message: \"The requested resource was not found.\",\n statusCode: 404,\n error: \"NOT_FOUND\",\n });\n }\n\n throw new APIError({\n message: \"We encountered an error trying to process your request. Please try again later\",\n statusCode: 520,\n error: \"UNKNOWN_ERROR\",\n });\n }\n}\n","import { z } from \"zod\";\nimport { initContract } from \"@ts-rest/core\";\nimport { ProfileDTOSchemas } from \"./profile/profile.dtos\";\n\n/**\n * Auth API contract\n */\nexport const authContract = initContract().router({\n logIn: {\n method: \"POST\",\n path: \"/login\",\n body: z.object({\n type: z.string().default(\"password\"),\n identity: z.string().email(),\n password: z.string(),\n }),\n responses: {\n 201: z.object({\n profile: ProfileDTOSchemas.profileDTOSchema,\n token: z.string(),\n access: z.array(z.string()),\n resetPassword: z.boolean(),\n }),\n 400: z.object({}),\n },\n },\n access: {\n method: \"GET\",\n path: \"/access\",\n headers: z.object({ token: z.string() }),\n responses: {\n 200: z.string().array(),\n },\n },\n resetPassword: {\n method: \"PUT\",\n path: \"/password\",\n body: z.object({\n currentPassword: z.string(),\n newPassword: z.string(),\n }),\n responses: {},\n },\n});\n","import { initContract } from \"@ts-rest/core\";\nimport { z } from \"zod\";\n\n/**\n * Identity API contract\n */\nexport const identityContract = initContract().router({\n getUserCredentials: {\n method: \"GET\",\n path: \"/me\",\n responses: {\n 200: z.object({\n name: z.string(),\n identity: z.string(),\n }),\n },\n },\n});\n","import type { AppRouteResponse } from \"@ts-rest/core\";\nimport { z } from \"zod\";\n\n/* Common 400 API Error Response */\ntype ApiErrorResponseSchemaType = z.ZodObject<{\n statusCode: z.ZodNumber;\n message: z.ZodString;\n error: z.ZodString;\n details: z.ZodOptional<z.ZodRecord>;\n}>;\nexport const common400ResponseSchema: ApiErrorResponseSchemaType = z.object({\n statusCode: z.number(),\n message: z.string(),\n error: z.string(),\n details: z.record(z.string()).optional(),\n});\nexport type Common400APIResponse = z.infer<typeof common400ResponseSchema>;\n\nexport const commonAPIResponses: Record<number, AppRouteResponse> = {\n 400: common400ResponseSchema,\n};\n","import { identityContract } from \"./identity.api-contract\";\nimport type { ClientInferResponseBody } from \"@ts-rest/core\";\nimport { BaseRepository } from \"@/lib/api\";\n\ntype GetUserIdentityResponse = ClientInferResponseBody<typeof identityContract.getUserCredentials>;\n\n/**\n * Repository class for retrieving user identity-related information.\n * Handles fetching user credentials and identity data from the API.\n *\n * This repository handles pure API communication without permission checking.\n * Permission validation should be handled at the service layer or route handlers.\n *\n * @extends {BaseRepository<typeof identityContract>}\n *\n * @example\n * ```typescript\n * const repo = new IdentityRepository({ token: userToken });\n * const identity = await repo.getIdentity();\n * ```\n */\nexport class IdentityRepository extends BaseRepository<typeof identityContract> {\n /**\n * Creates an instance of IdentityRepository.\n *\n * @param options - Optional configuration\n * @param options.token - Authentication token for API calls\n * @param options.root - Custom API root URL\n *\n * @example\n * ```typescript\n * const repo = new IdentityRepository({\n * token: \"user-auth-token\",\n * root: \"base-api-url\"\n * });\n * ```\n */\n constructor(options?: { token?: string; root?: string }) {\n super(\"login\", identityContract, options);\n }\n\n /**\n * Retrieves the current user's login credentials and identity information.\n *\n * @returns Promise that resolves to the user's login credentials\n * @throws {APIError} If the request fails or returns an unexpected status\n *\n * @example\n * ```typescript\n * try {\n * const identity = await repo.getIdentity();\n * console.log(\"User identity:\", identity);\n * } catch (error) {\n * console.error(\"Failed to get user identity:\", error.message);\n * }\n * ```\n */\n async getIdentity(): Promise<GetUserIdentityResponse> {\n const result = await this.client.getUserCredentials();\n\n if (result.status === 200) {\n return result.body;\n }\n\n throw new Error(`Failed to get user identity. Status: ${result.status}`);\n }\n}\n","import { BaseRepository } from \"../../lib/api/base-repository\";\nimport { authContract } from \"./auth.contract\";\nimport { APIError } from \"@/lib/error\";\nimport { User } from \"@/models/user.model\";\nimport { IdentityRepository } from \"./identity/identity.repository\";\n\n/**\n * Repository class for handling authentication-related operations.\n * Provides methods for user login and password management.\n *\n * @extends {BaseRepository<typeof authContract>}\n *\n * @example\n * ```typescript\n * const authRepo = new AuthRepository();\n * const user = await authRepo.logIn(\"user@example.com\", \"password\");\n * ```\n */\nexport class AuthRepository extends BaseRepository<typeof authContract> {\n /**\n * Creates an instance of AuthRepository.\n *\n * @param options - Optional configuration\n * @param options.root - Custom API root URL\n * @param options.token - Authentication token for password operations\n */\n constructor(options?: { root?: string; token?: string }) {\n super(\"auth\", authContract, options);\n }\n\n /**\n * Authenticates a user with the provided email and password.\n *\n * @param email - The email of the user attempting to log in\n * @param password - The password of the user\n * @returns Promise that resolves to a User object on successful login\n * @throws {APIError} If the email or password is invalid, or if another error occurs during login\n *\n * @example\n * ```typescript\n * try {\n * const user = await authRepo.logIn(\"user@example.com\", \"securePassword\");\n * console.log(`Welcome ${user.name}!`);\n * } catch (error) {\n * console.error(\"Login failed:\", error.message);\n * }\n * ```\n */\n async logIn(email: string, password: string): Promise<User> {\n const body = { type: \"password\", identity: email, password };\n const result = await this.client.logIn({ body });\n\n if (result.status === 400) {\n throw new APIError({\n message: \"Invalid email or password\",\n statusCode: 400,\n });\n }\n\n if (result.status === 201) {\n const repo = new IdentityRepository({ token: result.body.token });\n const loginCredentials = await repo.getIdentity();\n const user = User.from({ ...result.body, ...loginCredentials });\n if (user) return user;\n }\n\n throw new APIError({\n message: \"An error occurred while trying to log in\",\n statusCode: 502,\n });\n }\n\n /**\n * Updates the current user's password.\n *\n * @param currentPassword - The user's current password\n * @param newPassword - The new password the user wants to set\n * @returns Promise that resolves to true if the password update is successful\n * @throws {APIError} If the current password is invalid or another error occurs during the update\n *\n * @example\n * ```typescript\n * try {\n * await authRepo.updatePassword(\"oldPassword\", \"newSecurePassword\");\n * console.log(\"Password updated successfully\");\n * } catch (error) {\n * console.error(\"Password update failed:\", error.message);\n * }\n * ```\n */\n async updatePassword(currentPassword: string, newPassword: string): Promise<boolean> {\n const result = await this.client.resetPassword({\n body: { currentPassword, newPassword },\n });\n\n if (result.status === 200) return true;\n\n if (result.status === 400) {\n throw new APIError({\n message: \"Invalid current password\",\n statusCode: 400,\n });\n }\n\n throw new APIError({\n message: \"An error occurred while trying to update password\",\n statusCode: 502,\n });\n }\n}\n","import { CountryCode } from \"@temboplus/frontend-core\";\nimport { Wallet } from \"../../models/wallet.model\";\n\nexport const WalletUtils = {\n /**\n * Gets unique countries from a list of wallets\n * @param wallets Array of wallets\n * @returns Array of unique country codes\n */\n getUniqueCountries(wallets: Wallet[]): CountryCode[] {\n const uniqueCountries = new Set<CountryCode>();\n wallets.forEach((wallet) => {\n if (wallet.countryCode) {\n uniqueCountries.add(wallet.countryCode as CountryCode);\n }\n });\n return Array.from(uniqueCountries);\n },\n\n /**\n * Gets wallets for a specific country\n * @param wallets Array of wallets\n * @param countryCode Country code to filter by\n * @returns Array of wallets for the specified country\n */\n getWalletsByCountry(wallets: Wallet[], countryCode: CountryCode): Wallet[] {\n return wallets.filter((wallet) => wallet.countryCode === countryCode);\n },\n};\n","import { z } from \"zod\";\nimport { initContract } from \"@ts-rest/core\";\nimport { Wallet } from \"../../models/wallet.model\";\nimport { WalletStatementEntry } from \"../../models/statement-entry.model\";\nimport { WalletDTOSchemas } from \"./wallet.dtos\";\n\nexport const contract = initContract().router({\n getWallets: {\n method: \"GET\",\n path: \"/\",\n query: WalletDTOSchemas.walletQuery,\n responses: {\n 200: z.array(Wallet.schema),\n },\n },\n getBalance: {\n method: \"POST\",\n path: \"/balance\",\n body: z.object({\n accountNo: z.string().optional(),\n }),\n responses: {\n 201: z.object({\n availableBalance: z.number(),\n }),\n },\n },\n getStatement: {\n method: \"POST\",\n path: \"/statement\",\n summary: \"Get Wallet Statement\",\n body: z.object({\n endDate: z.date(),\n startDate: z.date(),\n accountNo: z.string().optional(),\n }),\n responses: {\n 201: z.array(WalletStatementEntry.schema),\n },\n },\n});\n","import { Amount } from \"@temboplus/frontend-core\";\nimport { WalletStatementEntry } from \"../../models/statement-entry.model\";\nimport { Wallet } from \"../../models/wallet.model\";\nimport { WalletDTOSchemas } from \"./wallet.dtos\";\nimport { z } from \"zod\";\nimport { BaseRepository } from \"@/lib/api\";\nimport { contract } from \"./wallet.contract\";\n\n/**\n * Repository class for managing wallet operations including balance checking,\n * statement generation, and wallet information retrieval.\n *\n * This repository handles pure API communication without permission checking.\n * Permission validation should be handled at the service layer or route handlers.\n *\n * @extends {BaseRepository<typeof contract>}\n *\n * @example\n * ```typescript\n * // Direct token usage\n * const repo = new WalletRepository({ token: userToken });\n * const balance = await repo.getBalance({ wallet });\n *\n * // Service locator usage\n * const repo = new WalletRepository();\n * const wallets = await repo.getWallets();\n * ```\n */\nexport class WalletRepository extends BaseRepository<typeof contract> {\n /**\n * Creates an instance of WalletRepository initialized with the wallet contract.\n *\n * @param options - Optional configuration\n * @param options.root - Custom API root URL\n * @param options.token - Authentication token for API calls\n *\n * @example\n * ```typescript\n * const repo = new WalletRepository({\n * token: \"user-auth-token\",\n * root: \"base-api-url\"\n * });\n * ```\n */\n constructor(options?: { root?: string; token?: string }) {\n super(\"wallet\", contract, options);\n }\n\n /**\n * Retrieves the current available balance for a specific wallet.\n *\n * Supports two input methods:\n * 1. Direct wallet object (preferred for performance)\n * 2. Account number lookup (requires additional API call to fetch wallet details)\n *\n * @param props - The request properties (must provide either wallet or accountNo)\n * @param props.wallet - The wallet object for which to retrieve the balance (preferred method)\n * @param props.accountNo - Alternative: account number to lookup wallet and fetch balance\n * @returns Promise that resolves to the available balance as an Amount object\n * @throws {Error} If neither wallet nor accountNo is provided\n * @throws {Error} If accountNo is provided but no matching wallet is found\n * @throws {Error} If the balance fetch operation fails due to network or server errors\n *\n * @example\n * ```typescript\n * // Method 1: Using wallet object (recommended - faster)\n * try {\n * const wallet = await repo.getWallets().then(w => w[0]);\n * const balance = await repo.getBalance({ wallet });\n * console.log(`Available balance: ${balance.label}`);\n * } catch (error) {\n * console.error('Failed to fetch balance:', error.message);\n * }\n *\n * // Method 2: Using account number (requires wallet lookup)\n * try {\n * const balance = await repo.getBalance({ accountNo: '123456789' });\n * console.log(`Available balance: ${balance.label}`);\n * } catch (error) {\n * console.error('Failed to fetch balance:', error.message);\n * }\n * ```\n */\n async getBalance(props: { wallet?: Wallet; accountNo?: string }): Promise<Amount> {\n if (!props.wallet && !props.accountNo) {\n throw new Error(\"Either wallet or accountNo must be provided\");\n }\n\n // Method 1: Direct wallet object usage (preferred)\n if (props.wallet) {\n const result = await this.client.getBalance({\n body: { accountNo: props.wallet.accountNo },\n });\n\n if (result.status === 201) {\n const balance = result.body.availableBalance;\n const amount = Amount.from(balance, props.wallet.currencyCode);\n if (amount) return amount;\n }\n }\n\n // Method 2: Account number lookup (requires additional API call)\n if (props.accountNo) {\n const wallets = await this.getWallets({ accountNo: props.accountNo });\n if (wallets.length === 0) {\n throw new Error(`No wallet found for accountNo: ${props.accountNo}`);\n }\n const wallet = wallets[0];\n\n const result = await this.client.getBalance({\n body: { accountNo: props.accountNo },\n });\n\n if (result.status === 201) {\n const balance = result.body.availableBalance;\n const amount = Amount.from(balance, wallet.currencyCode);\n if (amount) return amount;\n }\n }\n\n throw new Error(\"Failed to fetch balance\");\n }\n\n /**\n * Retrieves all wallets associated with the authenticated user.\n *\n * Supports optional filtering through query parameters such as accountNo,\n * status, or currency filtering.\n *\n * @param query - Optional query parameters for filtering wallets\n * @param query.accountNo - Filter by specific account number\n * @param query.status - Filter by wallet status (active, inactive, etc.)\n * @param query.currencyCode - Filter by currency code\n * @returns Promise that resolves to an array of validated Wallet instances\n * @throws {Error} If the wallet fetch operation fails or data is invalid\n *\n * @example\n * ```typescript\n * // Get all wallets\n * const allWallets = await repo.getWallets();\n *\n * // Get specific wallet by account number\n * const specificWallet = await repo.getWallets({ accountNo: '123456789' });\n *\n * // Get wallets with multiple filters\n * const activeUSDWallets = await repo.getWallets({\n * status: 'active',\n * currencyCode: 'USD'\n * });\n * ```\n */\n async getWallets(query?: z.infer<typeof WalletDTOSchemas.walletQuery>): Promise<Wallet[]> {\n const result = await this.client.getWallets({ query });\n\n if (result.status === 200) {\n const rawWallets = result.body;\n\n try {\n return rawWallets.map((walletData) => {\n const wallet = Wallet.from(walletData);\n if (!wallet) {\n throw new Error(`Invalid wallet data: ${JSON.stringify(walletData)}`);\n }\n return wallet;\n });\n } catch (error) {\n console.error(\"[WalletRepository] Error processing wallet data:\", error);\n throw new Error(\"Failed to process wallet data from API\");\n }\n }\n\n throw new Error(`Failed to fetch wallets. Status: ${result.status}`);\n }\n\n /**\n * Retrieves wallet statement entries for a specified date range.\n *\n * Supports two input methods:\n * 1. Direct wallet object (preferred for performance and currency context)\n * 2. Account number lookup (requires additional API call to determine currency)\n *\n * If no date range is provided, defaults to the current month.\n * Returns statement entries with enhanced narration objects containing payout detection\n * and parsing capabilities.\n *\n * @param props - The statement request properties\n * @param props.wallet - The wallet to get statement for (preferred method)\n * @param props.accountNo - Alternative: account number to lookup wallet and fetch statement\n * @param props.range - Optional date range (defaults to current month)\n * @param props.range.startDate - Start date for statement period\n * @param props.range.endDate - End date for statement period\n * @returns Promise that resolves to an array of validated WalletStatementEntry instances with Narration objects\n * @throws {Error} If neither wallet nor accountNo is provided\n * @throws {Error} If accountNo is provided but no matching wallet is found\n * @throws {Error} If the statement fetch operation fails or data is invalid\n *\n * @example\n * ```typescript\n * // Method 1: Using wallet object (recommended)\n * const wallet = await repo.getWallets().then(w => w[0]);\n * const currentMonthEntries = await repo.getStatement({ wallet });\n *\n * // Method 2: Using account number\n * const entriesForAccount = await repo.getStatement({\n * accountNo: '123456789'\n * });\n *\n * // Custom date range with wallet\n * const customRangeEntries = await repo.getStatement({\n * wallet,\n * range: {\n * startDate: new Date('2024-01-01'),\n * endDate: new Date('2024-01-31')\n * }\n * });\n *\n * // Process payout transactions\n * const payoutEntries = currentMonthEntries.filter(entry => entry.isPayout);\n * payoutEntries.forEach(entry => {\n * console.log(`Payout ID: ${entry.payoutId}, Amount: ${entry.amountDebited.label}`);\n * });\n * ```\n */\n async getStatement(props: {\n range?: { startDate: Date; endDate: Date };\n wallet?: Wallet;\n accountNo?: string;\n }): Promise<WalletStatementEntry[]> {\n if (!props.wallet && !props.accountNo) {\n throw new Error(\"Either wallet or accountNo must be provided\");\n }\n\n // Calculate default date range (current month)\n const now = new Date();\n const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);\n const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0);\n\n const dateRange = props.range ?? { startDate: monthStart, endDate: monthEnd };\n\n let targetWallet: Wallet;\n let targetAccountNo: string;\n\n // Determine wallet and account number\n if (props.wallet) {\n targetWallet = props.wallet;\n targetAccountNo = props.wallet.accountNo;\n } else if (props.accountNo) {\n // Lookup wallet by account number\n const wallets = await this.getWallets({ accountNo: props.accountNo });\n if (wallets.length === 0) {\n throw new Error(`No wallet found for accountNo: ${props.accountNo}`);\n }\n targetWallet = wallets[0];\n targetAccountNo = props.accountNo;\n } else {\n throw new Error(\"Either wallet or accountNo must be provided\");\n }\n\n const requestBody = {\n ...dateRange,\n accountNo: targetAccountNo,\n };\n\n const result = await this.client.getStatement({ body: requestBody });\n\n if (result.status === 201) {\n const rawEntries = result.body.map((entry) => ({\n ...entry,\n currencyCode: targetWallet.currencyCode,\n }));\n\n const validEntries = rawEntries.reduce((acc: WalletStatementEntry[], entryData) => {\n const entry = WalletStatementEntry.create(entryData);\n if (entry) {\n acc.push(entry);\n } else {\n console.warn(\"[WalletRepository] Skipping invalid statement entry:\", entryData);\n }\n return acc;\n }, []);\n\n return validEntries;\n }\n\n throw new Error(`Failed to fetch statement. Status: ${result.status}`);\n }\n}\n","import { create } from \"zustand\";\nimport { createJSONStorage, persist } from \"zustand/middleware\";\nimport { Country, type CountryCode } from \"@temboplus/frontend-core\";\nimport { Wallet } from \"../../models/wallet.model\";\nimport { User } from \"../../models/user.model\";\nimport { WalletUtils } from \"./wallet.utils\";\nimport { WalletRepository } from \"./wallet.repository\";\n\n// --- Constants for Initial Placeholder State ---\n// These values MUST satisfy the types but are only placeholders\n// before the store is marked as initialized. They should not be\n// relied upon directly without checking isInitialized.\nconst initialSelectedWalletId = \"\"; // Placeholder ID\nconst initialSelectedCountryCode: CountryCode = \"TZ\"; // Placeholder/Default Country\n\n// --- Zustand Store Definition ---\n\n/**\n * Defines the shape of the persisted wallet session state.\n * IMPORTANT: Properties like selectedWalletId/selectedCountryCode are guaranteed\n * non-nullable and valid ONLY when isInitialized is true.\n */\ninterface WalletSession {\n userId: string;\n wallets: Wallet[]; // Guaranteed non-empty when isInitialized = true\n selectedWalletId: string; // Guaranteed valid when isInitialized = true\n selectedCountryCode: CountryCode; // Guaranteed valid when isInitialized = true\n lastSynced: Date | string; // Date object in runtime, string in storage\n isInitialized: boolean; // Crucial guard flag\n}\n\n/**\n * Defines the complete state shape including actions and internal getters.\n */\ninterface WalletState extends WalletSession {\n // Actions\n setWallets: (wallets: Wallet[]) => void;\n setSelectedWallet: (wallet: Wallet) => void; // Input must be valid Wallet\n setSelectedCountry: (countryCode: CountryCode) => void; // Input must be valid CountryCode\n reset: () => void;\n\n // Internal Getters (Assume called only when state is consistent and initialized)\n _getWalletsByCountry: (countryCode: CountryCode) => Wallet[];\n _getSelectedWallet: () => Wallet; // Throws if called when not initialized or inconsistent\n}\n\n// Create the Zustand store with persistence\nexport const walletsStore = create<WalletState>()(\n persist(\n (set, get) => ({\n // --- Initial State ---\n userId: \"\",\n wallets: [], // Starts empty\n selectedWalletId: initialSelectedWalletId, // Placeholder\n selectedCountryCode: initialSelectedCountryCode, // Placeholder/Default\n lastSynced: new Date(),\n isInitialized: false, // Starts uninitialized\n\n // --- Actions ---\n setWallets: (wallets: Wallet[]) => {\n const validatedWallets = wallets.filter((w) => Wallet.is(w));\n\n // --- Constraint Enforcement ---\n if (validatedWallets.length === 0) {\n throw new Error(\n \"setWallets received an empty or invalid wallet list. User must have at least one valid wallet.\"\n );\n }\n // --- End Constraint Enforcement ---\n\n const currentSelectedId = get().selectedWalletId;\n const isCurrentlyInitialized = get().isInitialized;\n\n let walletToSelect: Wallet;\n const currentWalletStillValid = validatedWallets.some((w) => w.id === currentSelectedId);\n\n if (isCurrentlyInitialized && currentWalletStillValid && currentSelectedId !== initialSelectedWalletId) {\n walletToSelect = validatedWallets.find((w) => w.id === currentSelectedId)!;\n if (!walletToSelect) {\n console.error(\"Inconsistency: currentWalletStillValid was true, but find failed.\");\n walletToSelect = validatedWallets[0];\n }\n } else {\n walletToSelect = validatedWallets[0];\n }\n\n // Set state and mark as initialized\n set({\n wallets: validatedWallets,\n selectedWalletId: walletToSelect.id,\n selectedCountryCode: walletToSelect.countryCode,\n lastSynced: new Date(),\n isInitialized: true, // Mark as initialized AFTER setting valid state\n });\n },\n\n setSelectedWallet: (wallet: Wallet) => {\n // Input is non-nullable Wallet\n if (!Wallet.is(wallet)) {\n throw new Error(\"setSelectedWallet called with an invalid Wallet object.\");\n }\n if (!get().wallets.some((w) => w.id === wallet.id)) {\n throw new Error(\n `Attempted to select wallet (ID: ${wallet.id}) which is not present in the current wallet list.`\n );\n }\n set({\n selectedWalletId: wallet.id,\n selectedCountryCode: wallet.countryCode,\n });\n },\n\n setSelectedCountry: (countryCode: CountryCode) => {\n // Input is non-nullable CountryCode\n const countryWallets = get()._getWalletsByCountry(countryCode);\n\n // --- Constraint Enforcement ---\n if (countryWallets.length === 0) {\n throw new Error(\n `Cannot select country ${countryCode}: No wallets found for this country (check if code is valid or state is consistent).`\n );\n }\n // --- End Constraint Enforcement ---\n\n set({\n selectedCountryCode: countryCode,\n selectedWalletId: countryWallets[0].id,\n });\n },\n\n reset: () => {\n // Reset to initial placeholder state and mark as uninitialized\n set({\n userId: \"\",\n wallets: [],\n selectedWalletId: initialSelectedWalletId,\n selectedCountryCode: initialSelectedCountryCode,\n lastSynced: new Date(),\n isInitialized: false, // Mark as uninitialized\n });\n },\n\n // --- Internal Getters ---\n _getWalletsByCountry: (countryCode: CountryCode) => {\n return get().wallets.filter((w) => w.countryCode === countryCode);\n },\n\n _getSelectedWallet: (): Wallet => {\n // Returns non-nullable Wallet, throws if unsafe\n const state = get();\n if (!state.isInitialized) {\n // Check guard flag first\n throw new Error(\"Cannot get selected wallet: Wallet session not initialized.\");\n }\n // If initialized, selectedWalletId should be valid (not the placeholder)\n if (state.selectedWalletId === initialSelectedWalletId) {\n throw new Error(\"Cannot get selected wallet: Selection is still placeholder (initialization incomplete?).\");\n }\n\n const wallet = state.wallets.find((w) => w.id === state.selectedWalletId);\n if (!wallet) {\n console.error(\"Inconsistent state details:\", {\n id: state.selectedWalletId,\n walletCount: state.wallets.length,\n });\n throw new Error(\n `Inconsistent state: Selected wallet ID '${state.selectedWalletId}' not found in wallet list.`\n );\n }\n return wallet;\n },\n }),\n {\n name: \"wallet-session-storage\",\n storage: createJSONStorage(() => sessionStorage, {\n reviver: (key, value) => {\n if (key === \"wallets\" && Array.isArray(value)) {\n return value\n .map((data) => {\n try {\n const instance = Wallet.from(data);\n if (!instance) {\n console.warn(\"Filtering out invalid wallet data during rehydration:\", data);\n return undefined;\n }\n return instance;\n } catch (error) {\n console.error(\"Error rehydrating Wallet instance from persisted data:\", data, error);\n return undefined;\n }\n })\n .filter((w): w is Wallet => w !== undefined);\n }\n if (key === \"lastSynced\" && typeof value === \"string\") {\n try {\n const date = new Date(value);\n return isNaN(date.getTime()) ? new Date() : date;\n } catch {\n return new Date();\n }\n }\n return value;\n },\n replacer: (key, value) => {\n if (key === \"lastSynced\" && value instanceof Date) {\n return value.toISOString();\n }\n if (key === \"wallets\" && Array.isArray(value)) {\n return value.map((walletInstance) =>\n walletInstance instanceof Wallet && typeof walletInstance.toObject === \"function\"\n ? walletInstance.toObject()\n : walletInstance\n );\n }\n return value;\n },\n }),\n partialize: (state) => ({\n userId: state.userId,\n wallets: state.wallets,\n selectedWalletId: state.selectedWalletId,\n selectedCountryCode: state.selectedCountryCode,\n lastSynced: state.lastSynced,\n isInitialized: state.isInitialized,\n }),\n }\n )\n);\n\n// --- WalletSessionManager (Primary Public Interface) ---\n\n/**\n * WalletSessionManager handles wallet-related operations and state management coordination.\n * It uses a Zustand store (`useWalletStore`) for state and interacts with WalletRepo.\n *\n * IMPORTANT: This manager enforces the invariant that an initialized user session\n * always has at least one wallet and a valid selection. Methods that return selected\n * data (`getSelectedWallet`, `getSelectedCountry`, etc.) will THROW AN ERROR if called\n * before the session is initialized via the `initialize()` method.\n */\nexport class WalletSessionManager {\n private repo: WalletRepository;\n private static _instance: WalletSessionManager;\n\n /**\n * @private\n */\n private constructor() {\n this.repo = new WalletRepository();\n }\n\n /**\n * Gets the singleton instance of WalletSessionManager.\n * @returns {WalletSessionManager} The singleton instance.\n */\n public static get instance(): WalletSessionManager {\n if (!this._instance) {\n this._instance = new WalletSessionManager();\n }\n return this._instance;\n }\n\n /**\n * Checks if the wallet session is initialized.\n * @returns {boolean} True if initialized, false otherwise.\n */\n public isInitialized(): boolean {\n // Direct access to state is safe for this boolean flag\n return walletsStore.getState().isInitialized;\n }\n\n /**\n * Initializes the wallet manager for the current user.\n * Fetches wallets if the store isn't already initialized.\n * Throws an error if the user is not authenticated or if the user is found to have no wallets.\n * @async\n * @returns {Promise<void>} Resolves when initialization is complete.\n * @throws {Error} If user not authenticated or user has no wallets.\n */\n public async initialize(user: User): Promise<void> {\n if (this.isInitialized()) {\n return; // Avoid re-initializing if already done\n }\n\n if (!user) {\n throw new Error(\"User must be authenticated to initialize wallet manager\");\n }\n\n // Fetch wallets - refreshWallets handles the non-empty check and calls setWallets\n await this.refreshWallets();\n // If refreshWallets succeeds, setWallets will have run and set isInitialized = true\n }\n\n /**\n * Fetches the latest wallet data from the server and updates the store.\n * Throws an error if the user has no wallets or if the fetch fails.\n * Ensures the store is updated and marked as initialized.\n * @async\n * @returns {Promise<void>} Resolves when refresh is complete.\n * @throws {Error} If fetch fails or user has no wallets.\n */\n public async refreshWallets(): Promise<void> {\n try {\n const wallets = await this.repo.getWallets();\n if (!wallets || wallets.length === 0) {\n throw new Error(\"User has no wallets associated with their account. Cannot proceed.\");\n }\n // setWallets handles validation, selection guarantee, and setting isInitialized=true\n walletsStore.getState().setWallets(wallets);\n } catch (error) {\n console.error(\"Failed to refresh wallets:\", error);\n // Optional: Could set an error state here if needed\n throw error; // Re-throw for callers\n }\n }\n\n // --- Getters (Enforce Initialization) ---\n\n /**\n * Gets all available wallets. Guaranteed non-empty after successful initialization.\n * @returns {Wallet[]} Non-empty array of Wallet instances.\n * @throws {Error} If called before initialization is complete.\n */\n public getWallets(): Wallet[] {\n const state = walletsStore.getState();\n if (!state.isInitialized) {\n // Check the guard\n throw new Error(\"Cannot get wallets: Wallet session not initialized.\");\n }\n // Post-init, wallets is guaranteed non-empty by setWallets logic\n return state.wallets;\n }\n\n /**\n * Gets unique available country codes based on current wallets.\n * @returns {CountryCode[]} Array of CountryCodes. Guaranteed non-empty after initialization.\n * @throws {Error} If called before initialization is complete.\n */\n public getCountries(): CountryCode[] {\n const wallets = this.getWallets(); // Uses the guarded getter\n // Since getWallets guarantees non-empty, getUniqueCountries will also return non-empty\n return WalletUtils.getUniqueCountries(wallets);\n }\n\n /**\n * Gets wallets for a specific country.\n * @param {CountryCode} countryCode The country code to filter by.\n * @returns {Wallet[]} Array of Wallet instances for the country. May be empty if no wallets match the code, but the overall list isn't empty.\n * @throws {Error} If called before initialization is complete.\n */\n public getWalletsByCountry(countryCode: CountryCode): Wallet[] {\n if (!this.isInitialized()) {\n // Check guard first\n throw new Error(\"Cannot get wallets by country: Wallet session not initialized.\");\n }\n // Delegate to internal getter which is safe once initialized\n return walletsStore.getState()._getWalletsByCountry(countryCode);\n }\n\n /**\n * Gets the currently selected wallet instance.\n * @returns {Wallet} The selected Wallet instance (guaranteed non-nullable).\n * @throws {Error} If called before initialization is complete or if state is inconsistent.\n */\n public getSelectedWallet(): Wallet {\n // Delegate to the internal getter which checks initialization and consistency\n return walletsStore.getState()._getSelectedWallet();\n }\n\n /**\n * Gets the currently selected country code.\n * @returns {CountryCode} The selected CountryCode (guaranteed non-nullable).\n * @throws {Error} If called before initialization is complete.\n */\n public getSelectedCountryCode(): CountryCode {\n const state = walletsStore.getState();\n if (!state.isInitialized) {\n // Check guard\n throw new Error(\"Cannot get selected country code: Wallet session not initialized.\");\n }\n // If initialized, the code is guaranteed by store logic\n return state.selectedCountryCode;\n }\n\n /**\n * Gets the currently selected Country object.\n * @returns {Country} The selected Country object (guaranteed non-nullable).\n * @throws {Error} If called before initialization or if Country.fromCode fails for the selected code.\n */\n public getSelectedCountry(): Country {\n const code = this.getSelectedCountryCode(); // Uses the guarded getter for the code\n try {\n const country = Country.fromCode(code);\n if (!country) {\n // Belt-and-braces check in case fromCode returns null/undefined\n throw new Error(`Country.fromCode returned invalid result for code: ${code}`);\n }\n return country;\n } catch (error) {\n console.error(`Failed to get Country object for code ${code}:`, error);\n // This indicates a problem with the Country class or an invalid code in the state\n throw new Error(`Failed to create Country object for selected code ${code}.`);\n }\n }\n\n // --- Actions ---\n\n /**\n * Changes the selected wallet in the store.\n * Throws error if the provided walletId is not found in the current list.\n * @param {string} walletId - The ID of the wallet to select.\n * @returns {void}\n * @throws {Error} If wallet with given ID is not found or if called before initialization.\n */\n public changeWallet(walletId: string): void {\n if (!this.isInitialized()) {\n throw new Error(\"Cannot change wallet: Wallet session not initialized.\");\n }\n const store = walletsStore.getState();\n const wallet = store.wallets.find((w) => w.id === walletId);\n if (!wallet) {\n throw new Error(`Cannot change wallet: Wallet with ID ${walletId} not found.`);\n }\n store.setSelectedWallet(wallet); // Use the action\n }\n\n /**\n * Changes the selected country in the store.\n * Automatically selects the first available wallet for that country.\n * Throws error if no wallets exist for the given countryCode or if called before initialization.\n * @param {CountryCode} countryCode - The country code to select.\n * @returns {void}\n * @throws {Error} If no wallets found for the country code or if called before initialization.\n */\n public changeCountry(countryCode: CountryCode): void {\n if (!this.isInitialized()) {\n throw new Error(\"Cannot change country: Wallet session not initialized.\");\n }\n // Delegate to the action, which contains internal checks\n walletsStore.getState().setSelectedCountry(countryCode);\n }\n\n /**\n * Resets the wallet store to initial (uninitialized) state.\n * Typically used during user logout.\n */\n public reset(): void {\n walletsStore.getState().reset();\n }\n}\n","import { AuthRepository } from \"./auth.repository\";\nimport { WalletSessionManager } from \"../wallet/wallet-manager.session\";\nimport { User } from \"@/models/user.model\";\nimport { Permission } from \"@/models/permission\";\nimport { authStore, getCurrentUser, getCurrentToken, useCurrentUser } from \"./auth.store\";\n\n/**\n * Clean authentication manager for client-side usage only.\n *\n * This class provides a centralized way to manage user authentication state,\n * including login, logout, and permission checking. It uses a singleton pattern\n * for client-side usage and directly interfaces with the unified auth store.\n *\n * **Architecture:**\n * - **Client-side**: Use the singleton instance via `AfloatAuth.instance`\n * - **Server-side**: Pass tokens directly to repositories (no auth manager needed)\n *\n * **Features:**\n * - Direct integration with unified auth store\n * - React hooks for reactive UI updates\n * - Permission checking utilities\n * - Automatic wallet session management\n *\n * @example\n * ```typescript\n * // Client-side usage\n * const auth = AfloatAuth.instance;\n * const user = await auth.logIn(email, password);\n *\n * // Server-side usage (no auth manager needed)\n * const walletRepo = new WalletRepository({ token: extractedToken });\n * const balance = await walletRepo.getBalance({ wallet });\n * ```\n */\nexport class AfloatAuth {\n /** Client-side singleton instance */\n private static _instance: AfloatAuth | null = null;\n\n /**\n * Private constructor to control instantiation.\n * Use the static instance getter instead.\n *\n * @private\n */\n private constructor() {}\n\n /**\n * Gets or creates the client-side singleton instance.\n *\n * @returns The client-side singleton instance\n *\n * @example\n * ```typescript\n * const auth = AfloatAuth.instance;\n * if (auth.currentUser) {\n * console.log(\"User is logged in\");\n * }\n * ```\n */\n public static get instance(): AfloatAuth {\n if (!AfloatAuth._instance) {\n AfloatAuth._instance = new AfloatAuth();\n }\n return AfloatAuth._instance;\n }\n\n /**\n * Gets the authentication repository for API operations.\n *\n * @private\n * @returns The auth repository instance with current token\n */\n private get repo(): AuthRepository {\n return new AuthRepository({ token: this.getUserToken() });\n }\n\n /**\n * Gets the current authentication token.\n *\n * @returns The current authentication token, or undefined if not authenticated\n *\n * @example\n * ```typescript\n * const token = auth.getUserToken();\n * if (token) {\n * const apiClient = new SomeRepository({ token });\n * }\n * ```\n */\n getUserToken(): string | undefined {\n return getCurrentToken();\n }\n\n /**\n * Gets the currently authenticated user.\n *\n * @returns The current user instance, or undefined if not authenticated\n *\n * @example\n * ```typescript\n * const user = auth.currentUser;\n * if (user) {\n * console.log(`Welcome, ${user.name}!`);\n * }\n * ```\n */\n get currentUser(): User | undefined {\n return getCurrentUser();\n }\n\n /**\n * Checks if a user is currently authenticated.\n *\n * @returns True if user is authenticated, false otherwise\n *\n * @example\n * ```typescript\n * if (auth.isAuthenticated) {\n * // User is logged in\n * showDashboard();\n * } else {\n * // User needs to log in\n * showLoginForm();\n * }\n * ```\n */\n get isAuthenticated(): boolean {\n return !!this.currentUser && !!this.getUserToken();\n }\n\n /**\n * React hook for accessing the current user in client-side components.\n *\n * This hook provides reactive updates when the user state changes,\n * making it perfect for React components that need to respond to\n * authentication state changes.\n *\n * @returns The current user with reactive updates, or undefined if not authenticated\n * @throws {Error} If called in a server environment\n *\n * @example\n * ```typescript\n * function UserProfile() {\n * const user = AfloatAuth.instance.useCurrentUser();\n *\n * if (!user) {\n * return <LoginForm />;\n * }\n *\n * return <div>Hello, {user.name}!</div>;\n * }\n * ```\n */\n useCurrentUser(): User | undefined {\n return useCurrentUser();\n }\n\n /**\n * Checks if the current user has a specific permission.\n *\n * @param perm - The permission to check\n * @returns True if the user has the permission, false otherwise\n *\n * @example\n * ```typescript\n * if (auth.checkPermission(Permission.ViewBalance)) {\n * // User can view wallet balance\n * const walletRepo = new WalletRepository({ token: auth.getUserToken() });\n * const balance = await walletRepo.getBalance(wallet);\n * } else {\n * console.log(\"User doesn't have permission to view balance\");\n * }\n * ```\n */\n checkPermission(perm: Permission): boolean {\n return this.currentUser?.can(perm) ?? false;\n }\n\n /**\n * Authenticates a user with email and password.\n *\n * On successful authentication:\n * - Clears any existing auth data\n * - Stores the new user and token atomically\n * - Initializes related services (wallet manager)\n *\n * @param email - The user's email address\n * @param password - The user's password\n * @returns Promise resolving to the authenticated user\n * @throws {Error} If authentication fails\n *\n * @example\n * ```typescript\n * try {\n * const user = await auth.logIn(\"user@example.com\", \"password123\");\n * console.log(\"Login successful!\");\n * router.push(\"/dashboard\");\n * } catch (error) {\n * console.error(\"Login failed:\", error.message);\n * setError(\"Invalid credentials\");\n * }\n * ```\n */\n async logIn(email: string, password: string): Promise<User> {\n if (!email || !password) {\n throw new Error(\"Email and password are required\");\n }\n\n // Use AuthRepository without token for login\n const authRepo = new AuthRepository();\n const user = await authRepo.logIn(email, password);\n\n // Clear existing data and set new user and token atomically\n this.clearSavedData();\n authStore.getState().setUserAndToken(user, user.token);\n\n // Initialize related services\n try {\n await WalletSessionManager.instance.initialize(user);\n } catch (error) {\n console.warn(\"Failed to initialize wallet session manager:\", error);\n throw error;\n }\n\n return user;\n }\n\n /**\n * Updates the current user's password.\n *\n * @param currentPassword - The user's current password\n * @param newPassword - The new password to set\n * @returns Promise resolving to true if successful\n * @throws {Error} If the password update fails or user is not authenticated\n *\n * @example\n * ```typescript\n * try {\n * await auth.resetPassword(\"oldPassword\", \"newPassword123\");\n * console.log(\"Password updated successfully\");\n * showSuccessMessage(\"Password updated!\");\n * } catch (error) {\n * console.error(\"Password update failed:\", error.message);\n * showErrorMessage(\"Failed to update password\");\n * }\n * ```\n */\n async resetPassword(currentPassword: string, newPassword: string): Promise<boolean> {\n if (!this.isAuthenticated) {\n throw new Error(\"User must be authenticated to reset password\");\n }\n\n if (!currentPassword || !newPassword) {\n throw new Error(\"Current password and new password are required\");\n }\n\n await this.repo.updatePassword(currentPassword, newPassword);\n\n // Clear data to force re-authentication with new password\n this.clearSavedData();\n\n return true;\n }\n\n /**\n * Logs out the current user and clears all authentication data.\n *\n * This method:\n * - Clears user state from memory and storage\n * - Removes authentication tokens\n * - Resets related services\n *\n * @example\n * ```typescript\n * auth.logOut();\n * router.push(\"/login\");\n * console.log(\"User logged out successfully\");\n * ```\n */\n logOut(): void {\n this.clearSavedData();\n }\n\n /**\n * Refreshes the current authentication state.\n * Useful for clearing potentially stale data.\n *\n * @example\n * ```typescript\n * // Clear current state and force fresh authentication\n * auth.refresh();\n * ```\n */\n refresh(): void {\n this.clearSavedData();\n }\n\n /**\n * Gets debug information about the current authentication state.\n * Useful for troubleshooting authentication issues.\n *\n * @returns Object containing authentication state information\n *\n * @example\n * ```typescript\n * const debugInfo = auth.getDebugInfo();\n * console.log(\"Auth Debug Info:\", debugInfo);\n * ```\n */\n getDebugInfo() {\n return {\n hasUser: !!this.currentUser,\n hasToken: !!this.getUserToken(),\n isAuthenticated: this.isAuthenticated,\n userName: this.currentUser?.name || \"Not logged in\",\n tokenLength: this.getUserToken()?.length || 0,\n managerInstance: \"AfloatAuth singleton\",\n };\n }\n\n /**\n * Clears all stored authentication data from the unified store.\n *\n * @private\n */\n private clearSavedData(): void {\n // Use the unified store's clearAuth method\n authStore.getState().clearAuth();\n\n try {\n WalletSessionManager.instance.reset();\n } catch (error) {\n console.warn(\"Failed to reset wallet session manager:\", error);\n }\n }\n}\n","import { z } from \"zod\";\nimport { initContract } from \"@ts-rest/core\";\n\n/**\n * Auth API contract\n */\nexport const accessContract = initContract().router({\n getAccessList: {\n method: \"GET\",\n path: \"/access\",\n responses: {\n 200: z.string().array(),\n },\n },\n});\n","import { Profile } from \"@/models/profile.model\";\nimport { initContract } from \"@ts-rest/core\";\n\n/**\n * Profile API contract\n */\nexport const profileContract = initContract().router({\n getCurrentProfile: {\n method: \"GET\",\n path: \"/me\",\n responses: {\n 200: Profile.schema,\n },\n },\n});\n","import { z } from \"zod\";\nimport { initContract } from \"@ts-rest/core\";\nimport { ContactDTOSchemas } from \"./contact.dtos\";\nimport { commonAPIResponses } from \"@/lib/api\";\n\n/**\n * Contact API contract\n * Defines the REST endpoints for managing contacts\n *\n * @property {Object} postContact - Create a new contact (POST /)\n * @property {Object} editContact - Update an existing contact (PATCH /:id)\n * @property {Object} getContacts - Retrieve contacts list (GET /)\n * @property {Object} deleteContact - Delete a contact (DELETE /:id)\n */\nexport const contract = initContract().router(\n {\n createContact: {\n method: \"POST\",\n path: \"/\",\n body: ContactDTOSchemas.contactInputDTO,\n responses: {\n 201: ContactDTOSchemas.contactDTO,\n },\n },\n editContact: {\n method: \"PATCH\",\n path: \"/:id\",\n body: ContactDTOSchemas.contactInputDTO,\n responses: {\n 200: ContactDTOSchemas.contactDTO,\n },\n },\n getContacts: {\n method: \"GET\",\n path: \"/\",\n query: z.object({ orderByDesc: z.string() }),\n responses: {\n 200: z.array(ContactDTOSchemas.contactDTO),\n },\n },\n getByID: {\n method: \"GET\",\n path: \"/:id\",\n responses: {\n 200: ContactDTOSchemas.contactDTO,\n },\n },\n deleteContact: {\n method: \"DELETE\",\n path: \"/:id\",\n body: z.object({}),\n responses: {\n 200: z.object({}),\n },\n },\n },\n {\n commonResponses: commonAPIResponses,\n }\n);\n\n/**\n * Export type for use in client implementations\n */\nexport type ContactAPI = typeof contract;\n","import { MobileContactInfo, BankContactInfo, ContactInfo } from \"../../models/contact-info.model\";\nimport { ContactInputDTO, ContactType } from \"./contact.dtos\";\n\n/**\n * Interface for converting raw `ContactInput` into specific validated input types.\n */\ninterface ContactInputHandler {\n canHandle(info: ContactInfo): boolean;\n createInput(info: ContactInfo): ContactInputDTO;\n}\n\n/**\n * Handles and validates contact input of type `Mobile`.\n */\nclass MobileInputHandler implements ContactInputHandler {\n canHandle(info: ContactInfo): boolean {\n return info.type === \"Mobile\" && MobileContactInfo.is(info);\n }\n\n createInput(info: ContactInfo): ContactInputDTO {\n if (MobileContactInfo.is(info)) {\n return {\n type: ContactType.Mobile,\n displayName: info.accountName,\n accountNo: info.accountNumber,\n channel: info.channelId,\n //! Add countryCode when the API supports it\n // countryCode: info.countryCode,\n };\n }\n\n throw new Error(\"Expected Mobile Contact Info\");\n }\n}\n\n/**\n * Handles and validates contact input of type `Bank`.\n */\nclass BankInputHandler implements ContactInputHandler {\n canHandle(info: ContactInfo): boolean {\n return info.type === \"Bank\" && BankContactInfo.is(info);\n }\n\n createInput(info: ContactInfo): ContactInputDTO {\n if (BankContactInfo.is(info)) {\n return {\n type: ContactType.Bank,\n displayName: info.accountName,\n accountNo: info.accountNumber,\n channel: info.channelId,\n //! Add countryCode when the API supports it\n // countryCode: info.countryCode,\n };\n }\n\n throw new Error(\"Expected Mobile Contact Info\");\n }\n}\n\n/**\n * Factory for resolving and validating a raw `ContactInput` into a typed and valid version.\n */\nexport class ValidatedContactInputFactory {\n private handlers: ContactInputHandler[] = [new MobileInputHandler(), new BankInputHandler()];\n\n /**\n * Resolves a raw contact input into a valid and strongly-typed `ContactInput`.\n *\n * @param {ContactInputDTO} data - The unvalidated input object\n * @returns {ContactInputDTO} - A valid and converted input ready for use\n * @throws {Error} - If the input cannot be handled by any handler\n */\n resolve(data: ContactInfo): ContactInputDTO {\n const handler = this.handlers.find((h) => h.canHandle(data));\n\n if (!handler) {\n throw new Error(`Please check your data and try again`);\n }\n\n return handler.createInput(data);\n }\n}\n","import { APIError } from \"../../lib/error\";\nimport { initContract } from \"@ts-rest/core\";\nimport { z } from \"zod\";\nimport { PayoutDTOSchemas } from \"./payout.dtos\";\n\n/** Default eager loading settings for payout API */\nexport const DEFAULT_PAYOUT_API_EAGER = \"[createdBy,actionedBy]\";\n\n/** Default sort order for payout listings */\nexport const DEFAULT_ORDER_BY_DESC = \"createdAt\";\n\n/**\n * Payout management API contract\n * Defines endpoints for creating and managing payouts\n *\n * @property {Object} getPayouts - List payouts with filtering (GET /)\n * @property {Object} getPayoutsByApprovalStatus - List payouts by approval status (GET /)\n * @property {Object} postPayout - Create new payout (POST /)\n * @property {Object} approve - Approve/reject payout (POST /:id/approve)\n */\nexport const contract = initContract().router({\n getPayouts: {\n method: \"GET\",\n path: \"\",\n query: z.object({\n rangeStart: z.number(),\n rangeEnd: z.number(),\n eager: z.string(),\n approvalStatus: PayoutDTOSchemas.PayoutApprovalStatus.nullable().optional(),\n orderByDesc: z.string(),\n msisdn: z.string().optional(),\n }),\n responses: {\n 200: z.object({\n results: z.array(PayoutDTOSchemas.PayoutDTO),\n total: z.number(),\n }),\n },\n },\n getPayoutsByApprovalStatus: {\n method: \"GET\",\n path: \"\",\n query: z.object({\n rangeStart: z.number(),\n rangeEnd: z.number(),\n eager: z.string(),\n approvalStatus: PayoutDTOSchemas.PayoutApprovalStatus,\n orderByDesc: z.string(),\n }),\n responses: {\n 200: z.object({\n results: z.array(PayoutDTOSchemas.PayoutDTO),\n total: z.number(),\n }),\n },\n },\n postPayout: {\n method: \"POST\",\n path: \"\",\n body: PayoutDTOSchemas.PayoutInputDTO,\n responses: {\n 201: PayoutDTOSchemas.PayoutDTO,\n 400: APIError.schema,\n },\n },\n approve: {\n method: \"POST\",\n path: \"/:id/approve\",\n body: z.object({\n action: z.enum([\"Approve\", \"Reject\"]),\n notes: z.string().optional(),\n }),\n responses: {\n 201: PayoutDTOSchemas.PayoutDTO,\n 404: z.object({}),\n 409: z.object({}),\n },\n },\n getPayout: {\n method: \"GET\",\n path: \"/:id/\",\n responses: {\n 200: PayoutDTOSchemas.PayoutDTO,\n 404: z.object({}),\n },\n },\n});\n\n/**\n * Export type for use in client implementations\n */\nexport type PayoutAPI = typeof contract;\n","import {\n Amount,\n Country,\n ISO2CountryCode,\n MNOId,\n MNOUtils,\n PhoneNumber,\n PhoneNumberFactory,\n PhoneNumberFormat,\n TZMNOId,\n} from \"@temboplus/frontend-core\";\nimport { PayoutChannel, PayoutInputDTO } from \"./payout.dtos\";\nimport { ContactInfo, MobileContactInfo, BankContactInfo } from \"../../models/contact-info.model\";\nimport { Wallet } from \"@/models/wallet.model\";\nimport { Narration } from \"@/models\";\n\n/**\n * Valid payout channel codes that can be used in the system\n *\n * @remarks\n * - `${countryCode}-BANK-B2C`: Code for bank transfers\n * - `${countryCode}-${telecom}-B2C`: Pattern for mobile money transfers where the telecom company is the provider code\n *\n * @see {@link PayoutChannelCodeFactory} for functions to generate valid codes\n */\nexport type PayoutChannelCode = `${string}-BANK-B2C` | `${string}-${string}-B2C`;\n\n/**\n * Interface for payout channel handlers\n */\ninterface PayoutChannelHandler {\n canHandle(channel: PayoutChannel, receiver: ContactInfo, wallet: Wallet): boolean;\n createInput(args: { receiver: ContactInfo; amount: Amount; notes?: string }, wallet: Wallet): PayoutInputDTO;\n}\n\n/**\n * Constants used throughout the payout system\n */\nconst PAYOUT_CONSTANTS = {\n SUPPORTED_COUNTRY_CODES: [\"TZ\", \"KE\"] as ISO2CountryCode[],\n ERROR_MESSAGES: {\n INVALID_MOBILE_CONTACT: \"Invalid mobile contact info\",\n INVALID_BANK_CONTACT: \"Invalid bank contact info\",\n INVALID_PHONE_NUMBER: \"Invalid phone number\",\n INELIGIBLE_FOR_PAYOUT:\n \"The provided phone number is not eligible for payout. Please make sure it is a valid mobile number.\",\n MOBILE_NUMBER_MNO_MISMATCH: \"There is a mismatch between the phone number provided and the MNO used.\",\n UNSUPPORTED_COUNTRY_BANK: \"Only Tanzanian Banks are supported for now\",\n UNSUPPORTED_COUNTRY_MOBILE: \"Only Tanzanian mobile numbers are supported for now\",\n NO_HANDLER_FOUND: \"Cannot create payout input!\",\n },\n} as const;\n\n/**\n * Utility functions for processing payout input data\n */\nclass PayoutInputUtils {\n /**\n * Processes and validates notes input\n * @param notes - Raw notes string that may be undefined or contain whitespace\n * @returns Processed notes string or undefined if empty\n */\n static processNotes(notes?: string): string | undefined {\n if (!notes) return undefined;\n const trimmedNotes = notes.trim();\n return trimmedNotes.length > 0 ? trimmedNotes : undefined;\n }\n}\n\n/**\n * Factory for creating standardized payout channel codes\n *\n * @example\n * ```ts\n * // Create bank channel code\n * const bankCode = PayoutChannelCodeFactory.forBank(bankContactInfo); // Returns \"TZ-BANK-B2C\"\n *\n * // Create mobile channel code\n * const mobileCode = PayoutChannelCodeFactory.forMobile(phoneNumber); // Returns \"TZ-VODACOM-B2C\" for Vodacom number\n * ```\n */\nclass PayoutChannelCodeFactory {\n /**\n * Creates a bank transfer channel code\n * @param contactInfo - Bank contact information\n * @throws {Error} If country is not supported\n */\n static forBank(contactInfo: BankContactInfo, wallet: Wallet): PayoutChannelCode {\n if (!PAYOUT_CONSTANTS.SUPPORTED_COUNTRY_CODES.includes(contactInfo.countryCode)) {\n throw new Error(PAYOUT_CONSTANTS.ERROR_MESSAGES.UNSUPPORTED_COUNTRY_BANK);\n }\n return `${wallet.countryCode}-BANK-B2C`;\n }\n\n /**\n * Creates a mobile money channel code based on the telecom provider\n * @param phoneNumber - Phone number object containing telecom information\n * @returns {PayoutChannelCode} Channel code in format \"TZ-{TELECOM}-B2C\"\n * @throws {Error} If phone number is invalid or country is not supported\n *\n * @see {@link PhoneNumber} from \"@temboplus/frontend-core\" for phone number structure\n */\n static forMobile(phoneNumber: PhoneNumber, mnoId: MNOId, wallet: Wallet): PayoutChannelCode {\n if (!PhoneNumber.is(phoneNumber)) {\n throw new Error(PAYOUT_CONSTANTS.ERROR_MESSAGES.INVALID_PHONE_NUMBER);\n }\n\n if (!PAYOUT_CONSTANTS.SUPPORTED_COUNTRY_CODES.includes(phoneNumber.countryCode)) {\n throw new Error(PAYOUT_CONSTANTS.ERROR_MESSAGES.UNSUPPORTED_COUNTRY_MOBILE);\n }\n\n const eligibleForPayout = PhoneNumberFactory.checkPayoutEligibility(phoneNumber);\n if (!eligibleForPayout) {\n throw new Error(PAYOUT_CONSTANTS.ERROR_MESSAGES.INELIGIBLE_FOR_PAYOUT);\n }\n\n const operatorInfo = MNOUtils.getMNOById(mnoId, phoneNumber.countryCode);\n if (!operatorInfo) {\n throw new Error(PAYOUT_CONSTANTS.ERROR_MESSAGES.MOBILE_NUMBER_MNO_MISMATCH);\n }\n\n /**\n * Special routing: Channel all Vodacom numbers to Tigo\n * @note This is a specific business requirement from Mr. Tesha to resolve operational issues\n */\n if (operatorInfo.id === TZMNOId.VODACOM) {\n return `${wallet.countryCode}-${TZMNOId.TIGO.toString().toUpperCase()}-B2C` as PayoutChannelCode;\n }\n\n return `${wallet.countryCode}-${operatorInfo.id.toString().toUpperCase()}-B2C` as PayoutChannelCode;\n }\n}\n\n/**\n * Handler for mobile money payouts\n */\nclass MobileMoneyPayoutHandler implements PayoutChannelHandler {\n canHandle(channel: PayoutChannel, receiver: ContactInfo, wallet: Wallet): boolean {\n return (\n channel === PayoutChannel.MOBILE &&\n MobileContactInfo.is(receiver) &&\n wallet.countryCode === receiver.countryCode &&\n wallet.currencyCode === Country.from(receiver.countryCode)?.currencyCode\n );\n }\n\n createInput(args: { receiver: ContactInfo; amount: Amount; notes?: string }, wallet: Wallet): PayoutInputDTO {\n const receiver = args.receiver as MobileContactInfo;\n const processedNotes = PayoutInputUtils.processNotes(args.notes);\n const phoneNumber = receiver.phoneNumber;\n\n return {\n channel: PayoutChannelCodeFactory.forMobile(phoneNumber, receiver.mnoId, wallet),\n msisdn: phoneNumber.getWithFormat(PhoneNumberFormat.INTERNATIONAL_NUMERIC),\n description: processedNotes ?? Narration.generateDefaultPayoutNarration(receiver),\n payeeName: receiver.name,\n notes: processedNotes,\n amount: args.amount.numericValue,\n };\n }\n}\n\n/**\n * Handler for bank transfer payouts\n */\nclass BankTransferPayoutHandler implements PayoutChannelHandler {\n canHandle(channel: PayoutChannel, receiver: ContactInfo, wallet: Wallet): boolean {\n return (\n channel === PayoutChannel.BANK &&\n BankContactInfo.is(receiver) &&\n wallet.countryCode === receiver.countryCode &&\n wallet.currencyCode === Country.from(receiver.countryCode)?.currencyCode\n );\n }\n\n createInput(args: { receiver: ContactInfo; amount: Amount; notes?: string }, wallet: Wallet): PayoutInputDTO {\n const receiver = args.receiver as BankContactInfo;\n const processedNotes = PayoutInputUtils.processNotes(args.notes);\n\n return {\n channel: PayoutChannelCodeFactory.forBank(receiver, wallet),\n msisdn: `${receiver.bank.swiftCode}:${receiver.accNo}`,\n description: processedNotes ?? Narration.generateDefaultPayoutNarration(receiver),\n payeeName: receiver.accName,\n notes: processedNotes,\n amount: args.amount.numericValue,\n };\n }\n}\n\n/**\n * Factory class for creating payout input DTOs based on channel and contact information\n *\n * @example\n * ```ts\n * const factory = new PayoutInputFactory();\n * const payoutInput = factory.getPayoutInput({\n * channel: PayoutChannel.MOBILE,\n * receiver: mobileContactInfo,\n * amount: new Amount(1000),\n * notes: \"Payment for services\"\n * });\n * ```\n */\nexport class PayoutInputFactory {\n private readonly handlers: PayoutChannelHandler[] = [new MobileMoneyPayoutHandler(), new BankTransferPayoutHandler()];\n\n /**\n * Creates a payout input DTO based on the provided parameters\n *\n * @param args - Configuration object containing channel, receiver, amount, and optional notes\n * @returns {PayoutInputDTO} The configured payout input data transfer object\n * @throws {Error} If no suitable handler is found for the specified channel and contact type\n */\n getPayoutInput(\n args: {\n channel: PayoutChannel;\n receiver: ContactInfo;\n amount: Amount;\n notes?: string;\n },\n wallet: Wallet\n ): PayoutInputDTO {\n const handler = this.handlers.find((h) => h.canHandle(args.channel, args.receiver, wallet));\n\n if (!handler) {\n throw new Error(PAYOUT_CONSTANTS.ERROR_MESSAGES.NO_HANDLER_FOUND);\n }\n\n return handler.createInput(\n {\n receiver: args.receiver,\n amount: args.amount,\n notes: args.notes,\n },\n wallet\n );\n }\n}\n\n/**\n * @deprecated Use PayoutChannelCodeFactory instead\n * @see {@link PayoutChannelCodeFactory}\n */\nexport const createPayoutChannelCode = {\n bank: PayoutChannelCodeFactory.forBank,\n mobile: PayoutChannelCodeFactory.forMobile,\n};\n","import { z } from \"zod\";\n\n/**\n * Type definition for password schema using Zod.\n * This is used as a TypeScript type helper for the actual schema implementation.\n */\ntype _PasswordType = z.ZodEffects<\n z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>,\n string,\n string\n>;\n\n// ====================== Custom Schema Definitions ====================== //\n\n/**\n * Custom password schema that enforces strong password requirements.\n * Password must contain:\n * - At least 8 characters\n * - At least one uppercase letter\n * - At least one lowercase letter\n * - At least one number\n * - At least one special character (!@#$%^&*()_+-=[]{}|;:,.<>?)\n * - No common passwords or sequential patterns\n */\nconst passwordSchema: _PasswordType = z\n .string()\n .min(8, \"Password must be at least 8 characters long\")\n .max(128, \"Password must not exceed 128 characters\")\n .regex(/[A-Z]/, \"Password must contain at least one uppercase letter\")\n .regex(/[a-z]/, \"Password must contain at least one lowercase letter\")\n .regex(/[0-9]/, \"Password must contain at least one number\")\n .regex(\n /[!@#$%^&*()_+\\-=\\[\\]{}|;:,.<>?]/,\n \"Password must contain at least one special character (!@#$%^&*()_+-=[]{}|;:,.<>?)\"\n )\n .refine((password) => {\n // Check for common weak patterns\n const weakPatterns = [\n /(.)\\1{2,}/, // Three or more consecutive identical characters\n /123456|654321|abcdef|qwerty|password|admin|user/i, // Common passwords\n /^[0-9]+$/, // Only numbers\n /^[a-zA-Z]+$/, // Only letters\n ];\n\n return !weakPatterns.some((pattern) => pattern.test(password));\n }, \"Password contains weak patterns. Avoid repeated characters, common words, or simple sequences\")\n .refine((password) => {\n // Check for sequential characters (e.g., \"abcd\", \"1234\")\n const hasSequential =\n /(?:abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz|012|123|234|345|456|567|678|789)/i.test(\n password\n );\n return !hasSequential;\n }, \"Password should not contain sequential characters\")\n .refine((password) => {\n // Check for keyboard patterns (e.g., \"qwer\", \"asdf\")\n const keyboardPatterns =\n /(?:qwer|wert|erty|rtyu|tyui|yuio|uiop|asdf|sdfg|dfgh|fghj|ghjk|hjkl|zxcv|xcvb|cvbn|vbnm)/i;\n return !keyboardPatterns.test(password);\n }, \"Password should not contain keyboard patterns\");\n\n// ====================== Schema Type Definitions ====================== //\n\n/**\n * Type definition for role schema using Zod.\n * This is used as a TypeScript type helper for the actual schema implementation.\n */\ntype _RoleType = z.ZodObject<{\n id: z.ZodString;\n name: z.ZodString;\n description: z.ZodOptional<z.ZodString>;\n access: z.ZodArray<z.ZodString>;\n createdAt: z.ZodString;\n updatedAt: z.ZodString;\n}>;\n\n/**\n * Type definition for managed user schema using Zod.\n * This is used as a TypeScript type helper for the actual schema implementation.\n */\ntype _ManagedUserType = z.ZodObject<{\n id: z.ZodString;\n name: z.ZodString;\n identity: z.ZodString;\n type: z.ZodString;\n profileId: z.ZodString;\n roleId: z.ZodString;\n resetPassword: z.ZodBoolean;\n isActive: z.ZodBoolean;\n isArchived: z.ZodBoolean;\n role: z.ZodOptional<z.ZodType<RoleDTO>>;\n createdAt: z.ZodString;\n updatedAt: z.ZodString;\n}>;\n\n/**\n * Type definition for create user request schema using Zod.\n * This is used as a TypeScript type helper for the actual schema implementation.\n */\ntype _CreateUserRequestType = z.ZodObject<{\n name: z.ZodString;\n identity: z.ZodString;\n password: z.ZodOptional<_PasswordType>;\n roleId: z.ZodOptional<z.ZodString>;\n resetPassword: z.ZodOptional<z.ZodBoolean>;\n}>;\n\n/**\n * Type definition for update user request schema using Zod.\n * This is used as a TypeScript type helper for the actual schema implementation.\n */\ntype _UpdateUserRequestType = z.ZodObject<{\n name: z.ZodOptional<z.ZodString>;\n roleId: z.ZodOptional<z.ZodString>;\n password: z.ZodOptional<_PasswordType>;\n resetPassword: z.ZodOptional<z.ZodBoolean>;\n isActive: z.ZodOptional<z.ZodBoolean>;\n}>;\n\n/**\n * Type definition for reset password request schema using Zod.\n * This is used as a TypeScript type helper for the actual schema implementation.\n */\ntype _ResetPasswordRequestType = z.ZodObject<{\n newPassword: z.ZodOptional<_PasswordType>;\n sendNotification: z.ZodOptional<z.ZodBoolean>;\n}>;\n\n/**\n * Type definition for create user response schema using Zod.\n * This is used as a TypeScript type helper for the actual schema implementation.\n */\ntype _CreateUserResponseType = z.ZodObject<{\n id: z.ZodString;\n name: z.ZodString;\n identity: z.ZodString;\n type: z.ZodString;\n profileId: z.ZodString;\n roleId: z.ZodString;\n isActive: z.ZodBoolean;\n isArchived: z.ZodBoolean;\n createdAt: z.ZodString;\n}>;\n\n// ====================== Query Parameter Schema Type Definition ====================== //\n\n/**\n * Type definition for managed user query parameters schema using Zod.\n * This is used as a TypeScript type helper for the actual schema implementation.\n */\ntype _ManagedUserQueryParamsType = z.ZodObject<{\n id: z.ZodOptional<z.ZodString>;\n name: z.ZodOptional<z.ZodString>;\n identity: z.ZodOptional<z.ZodString>;\n type: z.ZodOptional<z.ZodString>;\n profileId: z.ZodOptional<z.ZodString>;\n roleId: z.ZodOptional<z.ZodString>;\n resetPassword: z.ZodOptional<z.ZodNumber>;\n isActive: z.ZodOptional<z.ZodNumber>;\n isArchived: z.ZodOptional<z.ZodNumber>;\n createdAt: z.ZodOptional<z.ZodString>;\n updatedAt: z.ZodOptional<z.ZodString>;\n\n eager: z.ZodOptional<z.ZodString>;\n}>;\n\n// ====================== Schema Definitions ====================== //\n\n/**\n * Schema definition for a role.\n * Represents a user role with permissions in the system.\n *\n * @property {string} id - Unique identifier for the role\n * @property {string} name - Display name of the role\n * @property {string} description - Optional description of the role\n * @property {string[]} access - Array of permission strings\n * @property {string} createdAt - ISO datetime string of role creation\n * @property {string} updatedAt - ISO datetime string of last role update\n */\nconst roleSchema: _RoleType = z.object({\n id: z.string().min(1),\n name: z.string().min(1, \"Role name is required\"),\n description: z.string().optional(),\n access: z.array(z.string()),\n createdAt: z.string().datetime(\"Invalid creation timestamp\"),\n updatedAt: z.string().datetime(\"Invalid update timestamp\"),\n});\n\n/**\n * Schema definition for a managed user account.\n * Represents a user account from the admin management perspective.\n *\n * @property {string} id - Unique identifier for the user account\n * @property {string} name - Full name of the user\n * @property {string} identity - User's email address or phone number\n * @property {string} type - Type of user account\n * @property {string} profileId - ID of the associated profile\n * @property {string} roleId - ID of the assigned role\n * @property {boolean} resetPassword - Whether user must reset password on next login\n * @property {boolean} isActive - Whether the account is currently active\n * @property {Role} role - Full role object with permissions\n * @property {string} createdAt - ISO datetime string of account creation\n * @property {string} updatedAt - ISO datetime string of last account update\n */\nconst managedUserSchema: _ManagedUserType = z.object({\n id: z.string().min(1),\n name: z.string().min(1, \"User name is required\"),\n identity: z.string().email(\"Invalid email address\"),\n type: z.string().min(1, \"User type is required\"),\n profileId: z.string().min(1, \"Profile ID is required\"),\n roleId: z.string().min(1, \"Role ID is required\"),\n resetPassword: z.boolean(),\n isActive: z.boolean(),\n isArchived: z.boolean(),\n role: roleSchema.optional(),\n createdAt: z.string().datetime(\"Invalid creation timestamp\"),\n updatedAt: z.string().datetime(\"Invalid update timestamp\"),\n});\n\n/**\n * Schema definition for managed user query parameters.\n * Contains all primitive fields from ManagedUserData for filtering/searching users.\n * Excludes complex object fields like 'role' which are not suitable for query parameters.\n *\n * @property {string} id - Optional filter by user ID\n * @property {string} name - Optional filter by user name (partial match)\n * @property {string} identity - Optional filter by user identity (email/phone)\n * @property {string} type - Optional filter by user type\n * @property {string} profileId - Optional filter by profile ID\n * @property {string} roleId - Optional filter by role ID\n * @property {number} resetPassword - Optional filter by password reset requirement\n * @property {number} isActive - Optional filter by account active status\n * @property {number} isArchived - Optional filter by account archived status\n * @property {string} createdAt - Optional filter by creation date (ISO datetime)\n * @property {string} updatedAt - Optional filter by last update date (ISO datetime)\n */\nconst managedUserQueryParamsSchema: _ManagedUserQueryParamsType = z.object({\n id: z.string().min(1).optional(),\n name: z.string().min(1).optional(),\n identity: z.string().email(\"Invalid email address\").optional(),\n type: z.string().min(1).optional(),\n profileId: z.string().min(1).optional(),\n roleId: z.string().min(1).optional(),\n resetPassword: z.number().optional(),\n isActive: z.number().optional(),\n isArchived: z.number().optional(),\n createdAt: z.string().datetime(\"Invalid creation timestamp\").optional(),\n updatedAt: z.string().datetime(\"Invalid update timestamp\").optional(),\n\n eager: z.string().optional(),\n});\n\n/**\n * Schema definition for creating a new user account.\n * Defines the required and optional fields for user creation.\n *\n * @property {string} name - Full name of the user\n * @property {string} identity - User's email address\n * @property {string} password - Initial password for the account\n * @property {string} roleId - Optional role ID (defaults to system default)\n * @property {boolean} resetPassword - Optional flag to force password reset (defaults to true)\n */\nconst createUserRequestSchema: _CreateUserRequestType = z.object({\n name: z.string().min(1, \"User name is required\"),\n identity: z.string().email(\"Valid email address is required\"),\n password: passwordSchema.optional(),\n roleId: z.string().optional(),\n resetPassword: z.boolean().optional(),\n});\n\n/**\n * Schema definition for updating a user account.\n * All fields are optional for partial updates.\n *\n * @property {string} name - Optional updated name\n * @property {string} roleId - Optional updated role ID\n * @property {string} password - Optional new password\n * @property {boolean} resetPassword - Optional flag to force password reset\n * @property {boolean} isActive - Optional account activation status\n */\nconst updateUserRequestSchema: _UpdateUserRequestType = z.object({\n name: z.string().min(1, \"User name cannot be empty\").optional(),\n roleId: z.string().min(1, \"Role ID cannot be empty\").optional(),\n password: passwordSchema.optional(),\n resetPassword: z.boolean().optional(),\n isActive: z.boolean().optional(),\n});\n\n/**\n * Schema definition for resetting a user's password.\n * Both fields are optional with system defaults.\n *\n * @property {string} newPassword - Optional new password (random generated if not provided)\n * @property {boolean} sendNotification - Optional flag to send notification to user\n */\nconst resetPasswordRequestSchema: _ResetPasswordRequestType = z.object({\n newPassword: passwordSchema.optional(),\n sendNotification: z.boolean().optional(),\n});\n\n/**\n * Schema definition for the response when creating a user.\n * Returns basic user information without sensitive data.\n *\n * @property {string} id - Unique identifier for the created user\n * @property {string} name - Full name of the user\n * @property {string} identity - User's email address\n * @property {string} type - Type of user account\n * @property {string} profileId - ID of the associated profile\n * @property {string} roleId - ID of the assigned role\n * @property {boolean} isActive - Whether the account is active\n * @property {string} createdAt - ISO datetime string of account creation\n */\nconst createUserResponseSchema: _CreateUserResponseType = z.object({\n id: z.string(),\n name: z.string(),\n identity: z.string(),\n type: z.string(),\n profileId: z.string(),\n roleId: z.string(),\n isActive: z.boolean(),\n isArchived: z.boolean(),\n createdAt: z.string().datetime(),\n});\n\n// ====================== Schema Collections ====================== //\n\n/**\n * Collection of user management schemas for export.\n * Provides access to all user and role validation schemas.\n */\nexport const UserManagementDTOSchemas = {\n role: roleSchema,\n managedUser: managedUserSchema,\n managedUserQueryParams: managedUserQueryParamsSchema,\n createUserRequest: createUserRequestSchema,\n updateUserRequest: updateUserRequestSchema,\n resetPasswordRequest: resetPasswordRequestSchema,\n createUserResponse: createUserResponseSchema,\n password: passwordSchema, // Export password schema for reuse\n};\n\n// ====================== TypeScript Types ====================== //\n\n/**\n * TypeScript type for a validated role object.\n * Use this type for role instances that have been validated against the schema.\n */\nexport type RoleDTO = z.infer<typeof UserManagementDTOSchemas.role>;\n\n/**\n * TypeScript type for a create user request object.\n * Use this type for user creation requests that have been validated against the schema.\n */\nexport type CreateUserRequestDTO = z.infer<typeof UserManagementDTOSchemas.createUserRequest>;\n\n/**\n * TypeScript type for an update user request object.\n * Use this type for user update requests that have been validated against the schema.\n */\nexport type UpdateUserRequestDTO = z.infer<typeof UserManagementDTOSchemas.updateUserRequest>;\n\n/**\n * TypeScript type for a reset password request object.\n * Use this type for password reset requests that have been validated against the schema.\n */\nexport type ResetPasswordRequestDTO = z.infer<typeof UserManagementDTOSchemas.resetPasswordRequest>;\n\n/**\n * TypeScript type for a create user response object.\n * Use this type for user creation responses that have been validated against the schema.\n */\nexport type CreateUserResponseDTO = z.infer<typeof UserManagementDTOSchemas.createUserResponse>;\n\n/**\n * TypeScript type for managed user query parameters.\n * Use this type for query parameter objects that have been validated against the schema.\n * All fields are optional to allow flexible filtering and searching.\n */\nexport type ManagedUserQueryParamsDTO = z.infer<typeof UserManagementDTOSchemas.managedUserQueryParams>;\n","import { initContract } from \"@ts-rest/core\";\nimport { z } from \"zod\";\nimport { UserManagementDTOSchemas } from \"./admin.dtos\";\n\n// ====================== API Contract ====================== //\nconst c = initContract();\n\nexport const userManagementContract = c.router({\n // List all users\n getUsers: {\n method: \"GET\",\n path: \"/login\",\n query: UserManagementDTOSchemas.managedUserQueryParams,\n responses: {\n 200: z.array(UserManagementDTOSchemas.managedUser),\n 401: z.object({\n message: z.string().optional(),\n }),\n 403: z.object({\n message: z.string().optional(),\n }),\n },\n summary: \"List all user accounts\",\n description: \"Retrieve a list of all user accounts in the system\",\n },\n\n // Get user by ID\n getUser: {\n method: \"GET\",\n path: \"/login/:id\",\n pathParams: z.object({\n id: z.string(),\n }),\n query: UserManagementDTOSchemas.managedUserQueryParams,\n responses: {\n 200: UserManagementDTOSchemas.managedUser,\n 401: z.object({\n message: z.string().optional(),\n }),\n 403: z.object({\n message: z.string().optional(),\n }),\n 404: z.object({\n message: z.string().optional(),\n }),\n },\n summary: \"Get user account details\",\n description: \"Retrieve detailed information about a specific user account\",\n },\n\n // Create new user\n createUser: {\n method: \"POST\",\n path: \"/login\",\n body: UserManagementDTOSchemas.createUserRequest,\n responses: {\n 201: UserManagementDTOSchemas.createUserResponse,\n 400: z.object({\n message: z.string().optional(),\n errors: z.array(z.string()).optional(),\n }),\n 401: z.object({\n message: z.string().optional(),\n }),\n 403: z.object({\n message: z.string().optional(),\n }),\n 409: z.object({\n message: z.string().optional(),\n }),\n },\n summary: \"Create new user account\",\n description: \"Create a new user account with specified role and permissions\",\n },\n\n // Update user\n updateUser: {\n method: \"PATCH\",\n path: \"/login/:id\",\n pathParams: z.object({\n id: z.string(),\n }),\n body: UserManagementDTOSchemas.updateUserRequest,\n responses: {\n 200: UserManagementDTOSchemas.managedUser,\n 400: z.object({\n message: z.string().optional(),\n errors: z.array(z.string()).optional(),\n }),\n 401: z.object({\n message: z.string().optional(),\n }),\n 403: z.object({\n message: z.string().optional(),\n }),\n 404: z.object({\n message: z.string().optional(),\n }),\n },\n summary: \"Update user account\",\n description: \"Update user account information, role, status, or force password reset\",\n },\n\n // Archive user (soft delete)\n archiveUser: {\n method: \"POST\",\n path: \"/login/:id/archive\",\n pathParams: z.object({\n id: z.string(),\n }),\n body: z.object({}),\n responses: {\n 200: UserManagementDTOSchemas.managedUser,\n 401: z.object({\n message: z.string().optional(),\n }),\n 403: z.object({\n message: z.string().optional(),\n }),\n 404: z.object({\n message: z.string().optional(),\n }),\n },\n summary: \"Archive user account\",\n description: \"Archive (soft delete) a user account\",\n },\n\n // Archive user (soft delete)\n unArchiveUser: {\n method: \"POST\",\n path: \"/login/:id/unarchive\",\n pathParams: z.object({\n id: z.string(),\n }),\n body: z.object({}),\n responses: {\n 200: UserManagementDTOSchemas.managedUser,\n 401: z.object({\n message: z.string().optional(),\n }),\n 403: z.object({\n message: z.string().optional(),\n }),\n 404: z.object({\n message: z.string().optional(),\n }),\n },\n summary: \"Un-archive user account\",\n description: \"Un-archive (soft delete) a user account\",\n },\n\n // Reset user password\n resetPassword: {\n method: \"POST\",\n path: \"/login/:id/reset-password\",\n pathParams: z.object({\n id: z.string(),\n }),\n body: UserManagementDTOSchemas.resetPasswordRequest,\n responses: {\n 200: z.object({\n success: z.boolean(),\n }),\n 400: z.object({\n message: z.string().optional(),\n }),\n 401: z.object({\n message: z.string().optional(),\n }),\n 403: z.object({\n message: z.string().optional(),\n }),\n 404: z.object({\n message: z.string().optional(),\n }),\n },\n summary: \"Reset user password\",\n description: \"Reset a user's password and optionally send notification\",\n },\n\n // Get all roles\n getRoles: {\n method: \"GET\",\n path: \"/role\",\n responses: {\n 200: z.array(UserManagementDTOSchemas.role),\n 401: z.object({\n message: z.string().optional(),\n }),\n 403: z.object({\n message: z.string().optional(),\n }),\n },\n summary: \"List all roles\",\n description: \"Retrieve a list of all available roles in the system\",\n },\n\n // Get role by ID\n getRole: {\n method: \"GET\",\n path: \"/role/:id\",\n pathParams: z.object({\n id: z.string(),\n }),\n responses: {\n 200: UserManagementDTOSchemas.role,\n 401: z.object({\n message: z.string().optional(),\n }),\n 403: z.object({\n message: z.string().optional(),\n }),\n 404: z.object({\n message: z.string().optional(),\n }),\n },\n summary: \"Get role details\",\n description: \"Retrieve detailed information about a specific role\",\n },\n});\n\n/**\n * TypeScript type for the complete user management contract.\n * Use this type for strongly typed API client implementations.\n */\nexport type UserManagementContract = typeof userManagementContract;\n","import { BaseRepository } from \"@/lib/api/base-repository\";\nimport { accessContract } from \"./access.api-contract\";\n\n/**\n * Repository class for managing access-related operations.\n * Handles retrieving user access lists and permissions from the API.\n *\n * This repository handles pure API communication without permission checking.\n * Permission validation should be handled at the service layer or route handlers.\n *\n * @extends {BaseRepository<typeof accessContract>}\n *\n * @example\n * ```typescript\n * const repo = new AccessRepository({ token: userToken });\n * const accessList = await repo.getAccessList();\n * ```\n */\nexport class AccessRepository extends BaseRepository<typeof accessContract> {\n /**\n * Creates an instance of AccessRepository.\n *\n * @param options - Optional configuration\n * @param options.token - Authentication token for API calls\n * @param options.root - Custom API root URL\n *\n * @example\n * ```typescript\n * const repo = new AccessRepository({\n * token: \"user-auth-token\",\n * root: \"base-api-url\"\n * });\n * ```\n */\n constructor(options?: { token?: string; root?: string }) {\n super(\"auth\", accessContract, options);\n }\n\n /**\n * Retrieves the current user's access list.\n *\n * @returns Promise that resolves to an array of access permissions/roles\n * @throws {APIError} If the request fails or returns an unexpected status\n *\n * @example\n * ```typescript\n * try {\n * const accessList = await repo.getAccessList();\n * console.log(\"User has access to:\", accessList);\n * } catch (error) {\n * console.error(\"Failed to get access list:\", error.message);\n * }\n * ```\n */\n async getAccessList(): Promise<string[]> {\n const result = await this.client.getAccessList();\n\n if (result.status === 200) {\n return result.body;\n }\n\n throw new Error(`Failed to get access list. Status: ${result.status}`);\n }\n}\n","import { BaseRepository } from \"../../lib/api/base-repository\";\nimport { contract } from \"./contact.api-contract\";\nimport { ValidatedContactInputFactory } from \"./contact-input-handler\";\nimport { Contact } from \"../../models/contact.model\";\nimport { ContactDTO } from \"./contact.dtos\";\nimport { ContactInfo } from \"@/models\";\n\n/**\n * Repository class for managing Contact data through API interactions.\n * Handles contact creation, updates, deletion, and retrieval operations.\n *\n * This repository handles pure API communication without permission checking.\n * Permission validation should be handled at the service layer or route handlers.\n *\n * @extends {BaseRepository<typeof contract>}\n *\n * @example\n * ```typescript\n * const repo = new ContactRepository({ token: userToken });\n * const contacts = await repo.getAll();\n * ```\n */\nexport class ContactRepository extends BaseRepository<typeof contract> {\n /**\n * Creates an instance of ContactRepository using the contact contract.\n *\n * @param options - Optional configuration\n * @param options.token - Authentication token for API calls\n * @param options.root - Custom API root URL\n *\n * @example\n * ```typescript\n * const repo = new ContactRepository({\n * token: \"user-auth-token\",\n * root: \"https://api-staging.afloat.money/v1\"\n * });\n * ```\n */\n constructor(options?: { token?: string; root?: string }) {\n super(\"contact\", contract, options);\n }\n\n /**\n * Creates a new contact record.\n *\n * @param info - The data required to create a new contact\n * @returns Promise that resolves to the newly created contact\n * @throws {APIError} If the creation operation fails\n *\n * @example\n * ```typescript\n * const newContact = await repo.create({\n * name: \"John Doe\",\n * email: \"john@example.com\",\n * phone: \"+1234567890\"\n * });\n * ```\n */\n async create(info: ContactInfo): Promise<Contact> {\n const body = new ValidatedContactInputFactory().resolve(info);\n const result = await this.client.createContact({ body });\n const data = this.handleResponse<ContactDTO>(result, 201);\n return Contact.create(data);\n }\n\n /**\n * Updates an existing contact record by ID.\n *\n * @param id - The unique identifier of the contact to edit\n * @param info - The data to update the contact with\n * @returns Promise that resolves to the updated contact\n * @throws {APIError} If the update operation fails\n *\n * @example\n * ```typescript\n * const updatedContact = await repo.edit(\"contact-id\", {\n * name: \"Jane Doe\",\n * email: \"jane@example.com\"\n * });\n * ```\n */\n async edit(id: string, info: ContactInfo): Promise<Contact> {\n const body = new ValidatedContactInputFactory().resolve(info);\n const result = await this.client.editContact({\n params: { id },\n body,\n });\n const data = this.handleResponse<ContactDTO>(result, 200);\n return Contact.create(data);\n }\n\n /**\n * Deletes a contact record by ID.\n *\n * @param id - The unique identifier of the contact to remove\n * @returns Promise that resolves when the deletion is complete\n * @throws {APIError} If the deletion operation fails\n *\n * @example\n * ```typescript\n * await repo.remove(\"contact-id\");\n * console.log(\"Contact deleted successfully\");\n * ```\n */\n async remove(id: string): Promise<void> {\n const result = await this.client.deleteContact({ params: { id } });\n this.handleResponse<void>(result, 200);\n }\n\n /**\n * Retrieves all contacts.\n * Results are ordered in descending order by creation date by default.\n *\n * @returns Promise that resolves to an array of contacts\n * @throws {APIError} If the fetch operation fails\n *\n * @example\n * ```typescript\n * const contacts = await repo.getAll();\n * contacts.forEach(contact => console.log(`Contact: ${contact.name}`));\n * ```\n */\n async getAll(): Promise<Contact[]> {\n const query = { orderByDesc: \"createdAt\" };\n const result = await this.client.getContacts({ query });\n const data = this.handleResponse<ContactDTO[]>(result, 200);\n return Contact.createMany(data);\n }\n\n /**\n * Retrieves a contact by its unique identifier.\n *\n * @param id - The unique identifier of the contact to retrieve\n * @returns Promise that resolves to the requested contact\n * @throws {APIError} If the contact is not found or fetch operation fails\n *\n * @example\n * ```typescript\n * const contact = await repo.getByID(\"contact-id\");\n * console.log(`Contact: ${contact.name}, Email: ${contact.email}`);\n * ```\n */\n async getByID(id: string): Promise<Contact> {\n const result = await this.client.getByID({ params: { id } });\n const data = this.handleResponse<ContactDTO>(result, 200);\n return Contact.create(data);\n }\n}\n","import { BaseRepository } from \"@/lib/api\";\nimport { contract, type PayoutAPI } from \"./payout.api-contract\";\nimport { APIError } from \"@/lib/error\";\nimport { Payout } from \"../../models/payout.model\";\nimport { PayoutInputFactory } from \"./payout-channel-handler\";\nimport { Amount, CurrencyCode } from \"@temboplus/frontend-core\";\nimport { PayoutApprovalStatus, PayoutChannel } from \"./payout.dtos\";\nimport { ContactInfo } from \"../../models/contact-info.model\";\nimport { WalletSessionManager } from \"../wallet/wallet-manager.session\";\n\n/**\n * Arguments for retrieving payouts from the API.\n *\n * @interface GetPayoutsAPIArgs\n *\n * @property {number} [rangeStart] - The starting index for pagination. Defaults to 0.\n * @property {number} [rangeEnd] - The ending index for pagination. Defaults to 10.\n * @property {boolean} [pending] - Filter for pending payouts only. If true, returns only pending payouts.\n * @property {string} [msisdn] - Filter for msisdn. Should be properly formatted. {country_code}{compact_number} for phone numbers and {SWIFT_CODE}:{account_no}\n *\n * @example\n * ```typescript\n * // Get first 10 payouts\n * const args: GetPayoutsAPIArgs = {\n * rangeStart: 0,\n * rangeEnd: 10\n * };\n *\n * // Get only pending payouts\n * const pendingArgs: GetPayoutsAPIArgs = {\n * pending: true\n * };\n * ```\n */\nexport interface GetPayoutsAPIArgs {\n rangeStart?: number;\n rangeEnd?: number;\n pending?: boolean;\n msisdn?: string;\n}\n\n/**\n * Repository class for managing payout operations including creation, approval,\n * rejection, and retrieval of payouts.\n *\n * This repository handles pure API communication without permission checking.\n * Permission validation should be handled at the service layer or route handlers.\n *\n * @extends {BaseRepository<PayoutAPI>}\n *\n * @example\n * ```typescript\n * const repo = new PayoutRepository({ token: userToken });\n * const payouts = await repo.getAll();\n * ```\n */\nexport class PayoutRepository extends BaseRepository<PayoutAPI> {\n /**\n * Creates an instance of PayoutRepository initialized with the payout contract.\n *\n * @param options - Optional configuration\n * @param options.token - Authentication token for API calls\n * @param options.root - Custom API root URL\n *\n * @example\n * ```typescript\n * const repo = new PayoutRepository({\n * token: \"user-auth-token\"\n * });\n * ```\n */\n constructor(options?: { token?: string; root?: string }) {\n super(\"payout\", contract, options);\n }\n\n /**\n * Retrieves a paginated list of payouts with optional filtering for pending status.\n *\n * @param args - Optional arguments for filtering and pagination\n * @param args.rangeStart - Starting index for pagination (default: 0)\n * @param args.rangeEnd - Ending index for pagination (default: 10)\n * @param args.pending - Filter for pending payouts only\n * @param args.msisdn - Filter by MSISDN\n * @returns Promise that resolves to paginated payout results and total count\n * @throws {APIError} If range is invalid or if the fetch operation fails\n *\n * @example\n * ```typescript\n * // Get first 20 payouts\n * const result = await repo.getAll({\n * rangeStart: 0,\n * rangeEnd: 20\n * });\n *\n * // Get only pending payouts\n * const pendingResult = await repo.getAll({ pending: true });\n * ```\n */\n async getAll(args?: GetPayoutsAPIArgs): Promise<{\n results: Payout[];\n total: number;\n }> {\n const rangeStart = args?.rangeStart ?? 0;\n const rangeEnd = args?.rangeEnd ?? 10;\n const pendingStatus = PayoutApprovalStatus.PENDING;\n\n const query = {\n rangeStart,\n rangeEnd,\n eager: \"[createdBy,actionedBy]\",\n orderByDesc: \"createdAt\",\n };\n\n if (args?.pending) {\n Object.assign(query, { approvalStatus: pendingStatus });\n }\n\n if (args?.msisdn) {\n Object.assign(query, { msisdn: args.msisdn });\n }\n\n if (rangeEnd <= rangeStart) {\n throw new APIError({\n message: \"Invalid range: end-date must be greater than start-date\",\n statusCode: 400,\n });\n }\n\n const result = await this.client.getPayouts({ query: query });\n\n if (result.status === 200) {\n return {\n results: Payout.createMany(result.body.results),\n total: result.body.total,\n };\n }\n\n throw APIError.unknown(\"An error occurred while fetching payouts\");\n }\n\n /**\n * Creates a new payout with the provided input data.\n *\n * @param args - The payout creation data\n * @param args.channel - The payout channel to use\n * @param args.receiver - Contact information for the payout receiver\n * @param args.amount - The amount to pay out\n * @param args.notes - Optional notes for the payout\n * @returns Promise that resolves to the created payout\n * @throws {APIError} If the input is invalid or if the creation operation fails\n *\n * @example\n * ```typescript\n * const payout = await repo.pay({\n * channel: PayoutChannel.MOBILE_MONEY,\n * receiver: { name: \"John Doe\", phone: \"+255123456789\" },\n * amount: Amount.from(10000, \"TZS\"),\n * notes: \"Payment for services\"\n * });\n * ```\n */\n async pay(args: { channel: PayoutChannel; receiver: ContactInfo; amount: Amount; notes?: string }): Promise<Payout> {\n const { min: defaultMin, max: defaultMax } = Amount.getTransactionLimits(args.amount.currencyCode);\n if (args.amount.lessThan(defaultMin) || args.amount.greaterThan(defaultMax)) {\n throw new APIError({\n statusCode: 400,\n message: `Please make sure the amount is between ${defaultMin.label} - ${defaultMax.label}`,\n });\n }\n\n const wallet = WalletSessionManager.instance.getSelectedWallet();\n if (!wallet) throw new Error(\"You have not selected a wallet to process this payout\");\n\n const body = new PayoutInputFactory().getPayoutInput(args, wallet);\n const result = await this.client.postPayout({ body });\n\n if (result.status === 201) return Payout.create(result.body);\n if (result.status === 400) {\n throw new APIError(result.body);\n }\n\n throw APIError.unknown();\n }\n\n /**\n * Approves a payout with optional notes.\n *\n * @param id - The ID of the payout to approve\n * @param args - Optional arguments\n * @param args.notes - Optional notes for the approval\n * @returns Promise that resolves to the approved payout\n * @throws {APIError} If payout is not found, already approved, or if the operation fails\n *\n * @example\n * ```typescript\n * const approvedPayout = await repo.approve(\"payout-id\", {\n * notes: \"Approved after verification\"\n * });\n * ```\n */\n async approve(id: string, args?: { notes?: string }): Promise<Payout> {\n const result = await this.client.approve({\n params: { id },\n body: { action: \"Approve\", notes: args?.notes },\n });\n\n if (result.status === 201) {\n return Payout.create(result.body);\n }\n if (result.status === 404) {\n throw new APIError({ message: \"Payout not found\", statusCode: 404 });\n }\n if (result.status === 409) {\n throw new APIError({\n message: \"Payout already approved\",\n statusCode: 409,\n });\n }\n\n throw APIError.unknown();\n }\n\n /**\n * Rejects a payout with optional notes.\n *\n * @param id - The ID of the payout to reject\n * @param args - Optional arguments\n * @param args.notes - Optional notes for the rejection\n * @returns Promise that resolves to the rejected payout\n * @throws {APIError} If payout is not found, already rejected, or if the operation fails\n *\n * @example\n * ```typescript\n * const rejectedPayout = await repo.reject(\"payout-id\", {\n * notes: \"Insufficient documentation\"\n * });\n * ```\n */\n async reject(id: string, args?: { notes?: string }): Promise<Payout> {\n const result = await this.client.approve({\n params: { id },\n body: { action: \"Reject\", notes: args?.notes },\n });\n\n if (result.status === 201) {\n return Payout.create(result.body);\n }\n if (result.status === 404) {\n throw new APIError({ message: \"Payout not found\", statusCode: 404 });\n }\n if (result.status === 409) {\n throw new APIError({\n message: \"Payout already rejected\",\n statusCode: 409,\n });\n }\n\n throw APIError.unknown();\n }\n\n /**\n * Retrieves a payout by its ID.\n *\n * @param id - The ID of the payout to retrieve\n * @returns Promise that resolves to the payout instance if found\n * @throws {APIError} If the payout is not found or another error occurs\n *\n * @example\n * ```typescript\n * const payout = await repo.getByID(\"payout-id\");\n * console.log(`Payout status: ${payout.status}`);\n * ```\n */\n async getByID(id: string): Promise<Payout> {\n const result = await this.client.getPayout({\n params: { id },\n });\n\n if (result.status === 200) {\n return Payout.create(result.body);\n }\n if (result.status === 404) {\n throw new APIError({ message: \"Payout not found\", statusCode: 404 });\n }\n\n throw APIError.unknown();\n }\n}\n","import { BaseRepository } from \"@/lib/api/base-repository\";\nimport { profileContract } from \"./profile.api-contract\";\nimport { Profile } from \"@/models/profile.model\";\n\n/**\n * Repository class for managing user profile operations.\n * Handles retrieving and updating user profile information from the API.\n *\n * This repository handles pure API communication without permission checking.\n * Permission validation should be handled at the service layer or route handlers.\n *\n * @extends {BaseRepository<typeof profileContract>}\n *\n * @example\n * ```typescript\n * const repo = new ProfileRepository({ token: userToken });\n * const profile = await repo.getCurrentProfile();\n * ```\n */\nexport class ProfileRepository extends BaseRepository<typeof profileContract> {\n /**\n * Creates an instance of ProfileRepository.\n *\n * @param options - Optional configuration\n * @param options.token - Authentication token for API calls\n * @param options.root - Custom API root URL\n *\n * @example\n * ```typescript\n * const repo = new ProfileRepository({\n * token: \"user-auth-token\",\n * root: \"base-api-url\"\n * });\n * ```\n */\n constructor(options?: { token?: string; root?: string }) {\n super(\"profile\", profileContract, options);\n }\n\n /**\n * Retrieves the current user's profile information.\n *\n * @returns Promise that resolves to the user's profile\n * @throws {APIError} If the request fails, profile data is invalid, or returns an unexpected status\n *\n * @example\n * ```typescript\n * try {\n * const profile = await repo.getCurrentProfile();\n * console.log(`User: ${profile.name}, Email: ${profile.email}`);\n * } catch (error) {\n * console.error(\"Failed to get profile:\", error.message);\n * }\n * ```\n */\n async getCurrentProfile(): Promise<Profile> {\n const result = await this.client.getCurrentProfile();\n\n if (result.status === 200) {\n const profile = Profile.from(result.body);\n if (profile) {\n return profile;\n }\n throw new Error(\"Invalid profile data received from server\");\n }\n\n throw new Error(`Failed to get current profile. Status: ${result.status}`);\n }\n}\n","import { BaseRepository } from \"@/lib/api\";\nimport { userManagementContract } from \"./admin.contract\";\nimport type {\n CreateUserRequestDTO,\n CreateUserResponseDTO,\n ManagedUserQueryParamsDTO,\n ResetPasswordRequestDTO,\n RoleDTO,\n UpdateUserRequestDTO,\n} from \"./admin.dtos\";\nimport { Role } from \"@/models/role.model\";\nimport { ManagedUser, ManagedUserData } from \"@/models/managed-user.model\";\n\n/**\n * Repository class for managing user accounts through API interactions.\n * Handles user creation, updates, archiving, and role management.\n *\n * This repository handles pure API communication without permission checking.\n * Permission validation should be handled at the service layer or route handlers.\n *\n * @extends {BaseRepository<typeof userManagementContract>}\n *\n * @example\n * ```typescript\n * const repo = new UserManagementRepository({ token: adminToken });\n * const users = await repo.getAllUsers();\n * ```\n */\nexport class UserManagementRepository extends BaseRepository<typeof userManagementContract> {\n /**\n * Creates an instance of UserManagementRepository using the user management contract.\n *\n * @param options - Optional configuration\n * @param options.token - Authentication token for API calls\n * @param options.root - Custom API root URL\n *\n * @example\n * ```typescript\n * const repo = new UserManagementRepository({\n * token: \"admin-auth-token\",\n * root: \"https://api-staging.afloat.money/v1\"\n * });\n * ```\n */\n constructor(options?: { token?: string; root?: string }) {\n super(\"admin\", userManagementContract, options);\n }\n\n /**\n * Creates a new user account.\n *\n * @param input - The data required to create a new user account\n * @returns Promise that resolves to the newly created user response\n * @throws {APIError} If the creation operation fails\n *\n * @example\n * ```typescript\n * const newUser = await repo.createUser({\n * email: \"newuser@example.com\",\n * name: \"New User\",\n * roleId: \"role-id\"\n * });\n * ```\n */\n async createUser(input: CreateUserRequestDTO): Promise<CreateUserResponseDTO> {\n const result = await this.client.createUser({ body: input });\n return this.handleResponse<CreateUserResponseDTO>(result, 201);\n }\n\n /**\n * Updates an existing user account by ID.\n *\n * @param id - The unique identifier of the user account to update\n * @param input - The data to update the user account with\n * @returns Promise that resolves to the updated user account\n * @throws {APIError} If the update operation fails\n *\n * @example\n * ```typescript\n * const updatedUser = await repo.updateUser(\"user-id\", {\n * name: \"Updated Name\",\n * email: \"updated@example.com\"\n * });\n * ```\n */\n async updateUser(id: string, input: UpdateUserRequestDTO): Promise<ManagedUser> {\n const result = await this.client.updateUser({\n params: { id },\n body: input,\n });\n\n const data = this.handleResponse<ManagedUserData>(result, 200);\n const managedUser = ManagedUser.from(data);\n if (!managedUser) {\n throw new Error(\"Invalid user data received from server\");\n }\n return managedUser;\n }\n\n /**\n * Archives (soft deletes) a user account by ID.\n *\n * @param id - The unique identifier of the user account to archive\n * @returns Promise that resolves to the updated user object\n * @throws {APIError} If the archive operation fails\n *\n * @example\n * ```typescript\n * const archivedUser = await repo.archiveUser(\"user-id\");\n * ```\n */\n async archiveUser(id: string): Promise<ManagedUser> {\n const result = await this.client.archiveUser({ params: { id } });\n const data = this.handleResponse<ManagedUserData>(result, 201);\n const managedUser = ManagedUser.from(data);\n if (!managedUser) {\n throw new Error(\"Invalid user data received from server\");\n }\n return managedUser;\n }\n\n /**\n * Unarchives a previously archived user account by ID.\n *\n * @param id - The unique identifier of the user account to unarchive\n * @returns Promise that resolves to the updated user object\n * @throws {APIError} If the unarchive operation fails\n *\n * @example\n * ```typescript\n * const restoredUser = await repo.unArchiveUser(\"user-id\");\n * ```\n */\n async unArchiveUser(id: string): Promise<ManagedUser> {\n const result = await this.client.unArchiveUser({ params: { id } });\n const data = this.handleResponse<ManagedUserData>(result, 201);\n const managedUser = ManagedUser.from(data);\n if (!managedUser) {\n throw new Error(\"Invalid user data received from server\");\n }\n return managedUser;\n }\n\n /**\n * Resets a user's password.\n *\n * @param id - The unique identifier of the user account\n * @param input - Optional password reset configuration\n * @returns Promise that resolves to success status\n * @throws {APIError} If the password reset operation fails\n *\n * @example\n * ```typescript\n * const result = await repo.resetUserPassword(\"user-id\", {\n * sendEmail: true\n * });\n * ```\n */\n async resetUserPassword(id: string, input: ResetPasswordRequestDTO = {}): Promise<{ success: boolean }> {\n const result = await this.client.resetPassword({\n params: { id },\n body: input,\n });\n return this.handleResponse<{ success: boolean }>(result, 201);\n }\n\n /**\n * Retrieves all user accounts.\n * Results are ordered in descending order by creation date by default.\n *\n * @param query - Optional query parameters for filtering and eager loading\n * @returns Promise that resolves to an array of managed users\n * @throws {APIError} If the fetch operation fails\n *\n * @example\n * ```typescript\n * // Get all users with role information\n * const users = await repo.getAllUsers({ eager: \"role\" });\n *\n * // Get users with custom query\n * const activeUsers = await repo.getAllUsers({ status: \"active\" });\n * ```\n */\n async getAllUsers(query: ManagedUserQueryParamsDTO = { eager: \"role\" }): Promise<ManagedUser[]> {\n const result = await this.client.getUsers({ query });\n const data = this.handleResponse<ManagedUserData[]>(result, 200);\n return ManagedUser.createMany(data);\n }\n\n /**\n * Retrieves a specific user account by ID.\n *\n * @param id - The unique identifier of the user account to retrieve\n * @param query - Optional query parameters for eager loading\n * @returns Promise that resolves to the managed user\n * @throws {APIError} If the user is not found or fetch operation fails\n *\n * @example\n * ```typescript\n * const user = await repo.getUser(\"user-id\", { eager: \"role\" });\n * console.log(`User: ${user.name}, Role: ${user.role?.name}`);\n * ```\n */\n async getUser(id: string, query: ManagedUserQueryParamsDTO = { eager: \"role\" }): Promise<ManagedUser> {\n const result = await this.client.getUser({ params: { id }, query });\n const data = this.handleResponse<ManagedUserData>(result, 200);\n const managedUser = ManagedUser.from(data);\n if (!managedUser) {\n throw new Error(\"Invalid user data received from server\");\n }\n return managedUser;\n }\n\n /**\n * Retrieves all available roles in the system.\n *\n * @returns Promise that resolves to an array of roles\n * @throws {APIError} If the fetch operation fails\n *\n * @example\n * ```typescript\n * const roles = await repo.getAllRoles();\n * roles.forEach(role => console.log(`Role: ${role.name}`));\n * ```\n */\n async getAllRoles(): Promise<Role[]> {\n const result = await this.client.getRoles();\n const data = this.handleResponse<RoleDTO[]>(result, 200);\n return data.map((roleData) => {\n const role = Role.from(roleData);\n if (!role) {\n throw new Error(\"Invalid role data received from server\");\n }\n return role;\n });\n }\n\n /**\n * Retrieves a specific role by ID.\n *\n * @param id - The unique identifier of the role to retrieve\n * @returns Promise that resolves to the role\n * @throws {APIError} If the role is not found or fetch operation fails\n *\n * @example\n * ```typescript\n * const role = await repo.getRole(\"role-id\");\n * console.log(`Role: ${role.name}, Permissions: ${role.permissions.length}`);\n * ```\n */\n async getRole(id: string): Promise<Role> {\n const result = await this.client.getRole({ params: { id } });\n const data = this.handleResponse<RoleDTO>(result, 200);\n const role = Role.from(data);\n if (!role) {\n throw new Error(\"Invalid role data received from server\");\n }\n return role;\n }\n}\n","/**\n * Type guard to check if an unknown value is an Error object.\n *\n * @param {unknown} e - The value to check\n * @returns {boolean} True if the value is an Error object with valid stack and message properties\n *\n * @example\n * try {\n * throw new Error('Something went wrong');\n * } catch (e) {\n * if (isError(e)) {\n * console.log(e.message); // TypeScript knows e is Error\n * }\n * }\n *\n * @remarks\n * This function checks for the existence of both `stack` and `message` properties\n * and verifies they are strings, which are standard properties of Error objects.\n * This helps distinguish between actual Error instances and objects that might\n * partially match the Error interface.\n */\nexport function isError(e: unknown): e is Error {\n const error = e as Record<string, unknown>;\n return error && typeof error.stack === \"string\" &&\n typeof error.message === \"string\";\n}\n"],"names":["APIError","Error","statusCode","error","details","constructor","args","super","message","this","name","is","schema","safeParse","success","unknown","z","object","string","number","int","optional","Permissions","Profile","ViewCurrent","Update","Contact","View","List","Create","Delete","Payment","Payout","Approve","Transfer","Wallet","ViewBalance","ViewStatement","Role","ViewRoles","ViewRole","UserManagement","ViewUsers","ViewUser","CreateUser","UpdateUser","ArchiveUser","UnArchiveUser","ResetPassword","PermissionError","requiredPermissions","join","permissionSchema","union","enum","Object","values","literal","array","ContactType","contactTypeSchema","nativeEnum","contactInputSchema","displayName","min","accountNo","channel","type","ContactDTOSchemas","contactDTO","id","profileId","createdAt","datetime","updatedAt","merge","contactInputDTO","contactType","ContactInfoError","context","BaseContactInfo","countryCode","MobileContactInfo","phoneNumber","mnoId","Mobile","trim","operation","e164Format","validate","MNOUtils","requiresExplicitMNO","isValidMNOForCountry","validMNOs","getCountryMNOs","map","mno","getMNOByPhoneNumber","fromContactDTO","info","phoneNumberString","startsWith","phone","PhoneNumber","from","console","warn","fromPayoutDTO","CountryValidation","isISO2CountryCode","msisdn","defaultCountry","payeeName","obj","mobileContactInfo","phone_number","phoneObj","derivedMNO","validateMNOForPhoneNumber","getValidationDetails","errors","push","isValid","length","warnings","accountName","accountNumber","getWithFormat","PhoneNumberFormat","E164","accountNameLabel","accountNumberLabel","channelLabel","channelId","channelName","getMNOById","mobileMoneyService","BankContactInfo","accName","bank","accNo","Bank","BankValidation","validateAccountName","validateAccountNumber","swiftCode","getCountryFromSwiftCode","validateSwiftCode","text","split","swiftcode","bankContactInfo","accNumber","validAccName","validAccNumber","shortName","data","parse","Date","accNoLabel","accNameLabel","create","createMany","dataArray","createSafe","canConstruct","result","toJSON","PayoutChannel","PayoutStatus","PayoutApprovalStatus","AuthorizerSchema","identity","PayoutStatusSchema","PayoutApprovalStatusSchema","BasePayoutSchema","amount","coerce","description","notes","nullish","PayoutInputDTOSchema","extend","PayoutDTOSchemas","PayoutDTO","default","refine","code","currencyCode","CurrencyValidation","isCurrencyCode","status","statusMessage","partnerReference","date","actionedAt","approvalStatus","createdBy","actionedBy","PayoutInputDTO","PayoutAuthorizer","Amount","REJECTED","FAILED","PAID","PENDING","contactInfo","ProfileDTOSchemas","profileDTOSchema","firstName","lastName","email","autoApprove","boolean","_id","_firstName","_lastName","_displayName","_phone","_accountNo","_email","_autoApprove","getName","toObject","JSON","stringify","fromJSON","jsonString","maybeProfile","undefined","User","profile","token","resetPassword","access","accessMap","permission","forEach","perm","includes","can","key","e","parsedData","rawProfile","Array","isArray","makeOptional","transform","val","WalletDTOSchemas","walletDTO","walletQuery","ISO2CountryCodesSet","has","currency","Currency","CurrencyCodesSet","statementEntry","debitOrCredit","tranRefNo","narration","txnDate","errorMap","valueDate","amountCredited","amountDebited","balance","_profileId","_accountName","_channel","_countryCode","_currencyCode","_createdAt","_updatedAt","validationResult","flatten","createdAtDate","updatedAtDate","maybeWallet","ECOBANK_PREFIX","NARR_V2_PREFIX","BANK_NARR_PREFIX","LEGACY_BANK_NARR_PREFIX","MOBILE_NARR_PREFIX","LEGACY_MOBILE_NARR_PREFIX","Narration","mediumText","substring","shortText","generateDefaultPayoutNarration","generateMobilePayoutNarrationV2","generateBankPayoutNarrationV2","toUpperCase","getContactDetails","bankResult","getBankContactDetails","mobileResult","getMobileContactDetails","narr","replace","potentialSwift","slice","capitalizeFirstLetter","test","country","json","banks","BankService","getInstance","searchBanks","_","potentialPhone","username","TZMobileNumber","names","filter","n","toJson","version","fromJson","isNarrationJson","maybeNarration","str","charAt","toLowerCase","WalletStatementEntry","_debitOrCredit","_tranRefNo","_narration","_txnDate","_valueDate","_amountCredited","_amountDebited","_balance","narrationInstance","validationData","restData","toISOString","numericValue","isWalletStatementEntryJson","maybeJson","isAmountJson","maybeEntry","equals","other","clone","toString","isPositive","label","toJsonArray","entries","entry","fromJsonArray","jsonArray","item","creditedAmount","debitedAmount","balanceAmount","Number","isFinite","permissions","Set","hasPermission","UserEntity","canAny","some","p","canAll","every","ManagedUser","roleId","isActive","isArchived","role","isAccountActive","isAccountArchived","needsPasswordReset","getAccountStatus","color","getRoleName","getCreatedDate","toLocaleDateString","getLastUpdateInfo","diffMs","getTime","diffDays","Math","floor","Boolean","isZodObjectStrict","passthrough","issues","path","ZodIssueCode","catchall","any","ContractNoBody","Symbol","isAppRoute","recursivelyApplyOptions","router","options","fromEntries","value","_a","_b","_c","objectA","objectB","pathPrefix","headers","baseHeaders","assign","strictStatusCodes","validateResponseOnClient","responses","commonResponses","metadata","ContractPlainTypeRuntimeSymbol","initContract","endpoints","query","mutation","response","body","otherResponse","contentType","noBody","encodeQueryParamsJson","encodedValue","isNaN","encodeURIComponent","encodeQueryParams","keys","flatMap","tokeniseValue","v","idx","k","UnknownStatusError","knownResponseStatuses","tsRestFetchApi","async","route","method","validateResponse","fetchOptions","fetch","get","responseSchema","blob","createFormData","formData","FormData","appendToFormData","File","append","normalizeHeaders","getCompleteUrl","baseUrl","params","jsonQuery","pathParams","matched","insertParamsIntoPath","queryComponent","queryString","convertQueryParamsToUrlString","getRouteQuery","clientArgs","inputArgs","fetchApiArgs","extraHeaders","overrideClientOptions","cache","next","extraInputArgs","overriddenClientArgs","evaluateFetchApiArgs","apiFetcher","api","valueOrFunction","combinedHeaders","fetcherArgs","rawBody","rawQuery","credentials","signal","URLSearchParams","fetchApi","throwOnUnknownStatus","initClient","subRouter","createJSONStorage","getStorage","storage","_e","getItem","str2","reviver","Promise","then","setItem","newValue","replacer","removeItem","toThenable","fn","input","onFulfilled","_onRejected","_onFulfilled","catch","onRejected","persist","config","baseOptions","set","localStorage","serialize","deserialize","partialize","state","persistedState","currentState","hasHydrated","hydrationListeners","finishHydrationListeners","thenableSerialize","errorInSync","thenable","serializedValue","savedSetState","setState","configResult","stateFromStorage","hydrate","cb","postRehydrationCallback","onRehydrateStorage","call","bind","storageValue","deserializedStorageValue","migrate","migratedState","_a2","setOptions","newOptions","clearStorage","getOptions","rehydrate","onHydrate","add","delete","onFinishHydration","oldImpl","getInitialState","migrationResult","migrated","skipHydration","newImpl","authStore","user","getUser","jsonUser","setUser","getToken","setToken","setUserAndToken","clearAuth","window","sessionStorage","getCurrentToken","getState","BaseRepository","contract","endpoint","root","globalToken","client","clientConfig","uuidv4","handleResponse","successStatusCode","authContract","logIn","password","currentPassword","newPassword","identityContract","getUserCredentials","commonAPIResponses","record","IdentityRepository","getIdentity","AuthRepository","repo","loginCredentials","updatePassword","WalletUtils","getUniqueCountries","wallets","uniqueCountries","wallet","getWalletsByCountry","getWallets","getBalance","availableBalance","getStatement","summary","endDate","startDate","WalletRepository","props","rawWallets","walletData","now","monthStart","getFullYear","getMonth","monthEnd","dateRange","range","targetWallet","targetAccountNo","requestBody","reduce","acc","entryData","walletsStore","userId","selectedWalletId","selectedCountryCode","lastSynced","isInitialized","setWallets","validatedWallets","w","currentSelectedId","isCurrentlyInitialized","walletToSelect","currentWalletStillValid","find","setSelectedWallet","setSelectedCountry","countryWallets","_getWalletsByCountry","reset","_getSelectedWallet","walletCount","instance","walletInstance","WalletSessionManager","static","_instance","initialize","refreshWallets","getCountries","getSelectedWallet","getSelectedCountryCode","getSelectedCountry","Country","fromCode","changeWallet","walletId","store","changeCountry","AfloatAuth","getUserToken","currentUser","getCurrentUser","isAuthenticated","useCurrentUser","useStore","checkPermission","authRepo","clearSavedData","logOut","refresh","getDebugInfo","hasUser","hasToken","userName","tokenLength","managerInstance","accessContract","getAccessList","profileContract","getCurrentProfile","createContact","editContact","getContacts","orderByDesc","getByID","deleteContact","MobileInputHandler","canHandle","createInput","BankInputHandler","ValidatedContactInputFactory","handlers","resolve","handler","h","getPayouts","rangeStart","rangeEnd","eager","nullable","results","total","getPayoutsByApprovalStatus","postPayout","approve","action","getPayout","PAYOUT_CONSTANTS","SUPPORTED_COUNTRY_CODES","ERROR_MESSAGES","INVALID_PHONE_NUMBER","INELIGIBLE_FOR_PAYOUT","MOBILE_NUMBER_MNO_MISMATCH","UNSUPPORTED_COUNTRY_BANK","UNSUPPORTED_COUNTRY_MOBILE","NO_HANDLER_FOUND","PayoutInputUtils","processNotes","trimmedNotes","PayoutChannelCodeFactory","forBank","forMobile","PhoneNumberFactory","checkPayoutEligibility","operatorInfo","TZMNOId","VODACOM","TIGO","MobileMoneyPayoutHandler","receiver","MOBILE","processedNotes","INTERNATIONAL_NUMERIC","BankTransferPayoutHandler","BANK","PayoutInputFactory","getPayoutInput","createPayoutChannelCode","mobile","passwordSchema","max","regex","pattern","roleSchema","managedUserSchema","UserManagementDTOSchemas","managedUser","managedUserQueryParams","createUserRequest","updateUserRequest","resetPasswordRequest","sendNotification","createUserResponse","userManagementContract","getUsers","createUser","updateUser","archiveUser","unArchiveUser","getRoles","getRole","edit","remove","getAll","pendingStatus","pending","pay","defaultMin","defaultMax","getTransactionLimits","lessThan","greaterThan","reject","resetUserPassword","getAllUsers","getAllRoles","roleData","stack"],"mappings":"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","x_google_ignoreList":[17,18]}