@temboplus/afloat 0.1.77-beta.6 → 0.1.77-beta.8

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.
@@ -1,40 +1,74 @@
1
1
  import { ContactDTO, ContactType } from "@/modules/contact/contact.dtos.js";
2
2
  import { ContactInfo } from "./contact-info.model.js";
3
3
  /**
4
- * Contact class that wraps the Zod schema and provides additional functionality
4
+ * Contact class that wraps the Zod schema and provides additional functionality.
5
+ * Represents a contact entity with validation and type-safe access to contact information.
6
+ *
7
+ * @class Contact
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * // Preferred: Use static factory methods
12
+ * const contact = Contact.from(contactData);
13
+ *
14
+ * // From JSON
15
+ * const jsonContact = Contact.fromJSON(jsonString);
16
+ *
17
+ * // Type-safe access
18
+ * if (contact) {
19
+ * console.log(contact.displayName);
20
+ * console.log(contact.info?.channelName);
21
+ * }
22
+ * ```
5
23
  */
6
24
  export declare class Contact {
7
25
  private readonly data;
8
26
  /**
9
- * Private constructor - use static methods to create instances
27
+ * Private constructor - use static factory methods to create instances.
28
+ *
29
+ * @deprecated Use {@link Contact.from} or other static factory methods instead
30
+ * @private
31
+ * @param {ContactDTO} data - Validated contact data
10
32
  */
11
33
  private constructor();
12
34
  /**
13
- * Unique identifier for the contact
35
+ * Unique identifier for the contact.
36
+ *
37
+ * @returns {string} The contact's unique ID
14
38
  */
15
39
  get id(): string;
16
40
  /**
17
- * Profile identifier associated with this contact
41
+ * Profile identifier associated with this contact.
42
+ *
43
+ * @returns {string} The profile ID
18
44
  */
19
45
  get profileId(): string;
20
46
  /**
21
- * Display name of the contact
47
+ * Display name of the contact.
48
+ *
49
+ * @returns {string} The contact's display name
22
50
  */
23
51
  get displayName(): string;
24
52
  /**
25
- * Type of contact (Bank or Mobile)
53
+ * Type of contact (Bank or Mobile).
54
+ *
55
+ * @returns {ContactType} The contact type
26
56
  */
27
57
  get type(): ContactType;
28
58
  /**
29
- * Creation timestamp of the contact
59
+ * Creation timestamp of the contact.
60
+ *
61
+ * @returns {Date} The creation date
30
62
  */
31
63
  get createdAt(): Date;
32
64
  /**
33
- * Update timestamp of the contact
65
+ * Update timestamp of the contact.
66
+ *
67
+ * @returns {Date} The last update date
34
68
  */
35
69
  get updatedAt(): Date;
36
70
  /**
37
- * Detailed contact information based on contact type
71
+ * Detailed contact information based on contact type.
38
72
  *
39
73
  * @returns {ContactInfo | undefined} Contact information object:
40
74
  * - MobileContactInfo for mobile money contacts
@@ -42,12 +76,12 @@ export declare class Contact {
42
76
  * - undefined if contact information cannot be constructed
43
77
  *
44
78
  * @remarks
45
- * For mobile contacts, constructs from phone number
46
- * For bank contacts, constructs from SWIFT code and account number
79
+ * For mobile contacts, constructs from phone number.
80
+ * For bank contacts, constructs from SWIFT code and account number.
47
81
  */
48
82
  get info(): ContactInfo | undefined;
49
83
  /**
50
- * Account number for the contact
84
+ * Account number for the contact.
51
85
  *
52
86
  * @returns {string} Account number:
53
87
  * - For valid contacts, returns formatted account number from ContactInfo
@@ -55,12 +89,14 @@ export declare class Contact {
55
89
  */
56
90
  get accNo(): string;
57
91
  /**
58
- * Account name for the contact
59
- * Always returns the display name
92
+ * Account name for the contact.
93
+ * Always returns the display name.
94
+ *
95
+ * @returns {string} The account name
60
96
  */
61
97
  get accName(): string;
62
98
  /**
63
- * Label for the account number field based on contact type
99
+ * Label for the account number field based on contact type.
64
100
  *
65
101
  * @returns {string} Appropriate label:
66
102
  * - "Phone Number" for mobile contacts
@@ -69,7 +105,7 @@ export declare class Contact {
69
105
  */
70
106
  get accNoLabel(): string;
71
107
  /**
72
- * Label for the channel field based on contact type
108
+ * Label for the channel field based on contact type.
73
109
  *
74
110
  * @returns {string} Appropriate label:
75
111
  * - "Channel" for mobile contacts
@@ -78,7 +114,7 @@ export declare class Contact {
78
114
  */
79
115
  get channelLabel(): string;
80
116
  /**
81
- * Label for the account name field based on contact type
117
+ * Label for the account name field based on contact type.
82
118
  *
83
119
  * @returns {string} Appropriate label:
84
120
  * - "Full Name" for mobile contacts
@@ -86,26 +122,105 @@ export declare class Contact {
86
122
  * - "Display Name" as fallback
87
123
  */
88
124
  get accNameLabel(): string;
125
+ /**
126
+ * Human-readable channel name for the contact.
127
+ *
128
+ * @returns {string} The channel name (e.g., "M-Pesa", "CRDB") or empty string
129
+ */
89
130
  get channelName(): string;
90
131
  /**
91
- * Creates a Contact instance from raw data
92
- * @throws {ZodError} if validation fails
132
+ * Creates a Contact instance from raw data.
133
+ * This is the preferred method for creating Contact instances.
134
+ *
135
+ * @static
136
+ * @param {ContactDTO} data - Raw contact data to validate and wrap
137
+ * @returns {Contact | undefined} Contact instance or undefined if validation fails
138
+ *
139
+ * @example
140
+ * ```typescript
141
+ * const contact = Contact.from({
142
+ * id: "123",
143
+ * profileId: "profile-456",
144
+ * displayName: "John Doe",
145
+ * type: "Mobile",
146
+ * accountNo: "+255712345678",
147
+ * channel: "VODACOM",
148
+ * createdAt: "2024-01-01T00:00:00Z",
149
+ * updatedAt: "2024-01-01T00:00:00Z"
150
+ * });
151
+ * ```
152
+ */
153
+ static from(data: ContactDTO): Contact | undefined;
154
+ /**
155
+ * Creates a Contact instance from raw data, throwing on validation failure.
156
+ *
157
+ * @static
158
+ * @param {ContactDTO} data - Raw contact data to validate and wrap
159
+ * @returns {Contact} Contact instance
160
+ * @throws {ZodError} If validation fails
161
+ *
162
+ * @example
163
+ * ```typescript
164
+ * try {
165
+ * const contact = Contact.create(contactData);
166
+ * } catch (error) {
167
+ * console.error("Validation failed:", error);
168
+ * }
169
+ * ```
93
170
  */
94
171
  static create(data: ContactDTO): Contact;
95
172
  /**
96
- * Creates multiple Contact instances from an array of raw data
97
- * @throws {ZodError} if validation fails for any item
173
+ * Creates multiple Contact instances from an array of raw data.
174
+ *
175
+ * @static
176
+ * @param {ContactDTO[]} dataArray - Array of contact data
177
+ * @returns {Contact[]} Array of Contact instances
178
+ * @throws {ZodError} If validation fails for any item
179
+ *
180
+ * @example
181
+ * ```typescript
182
+ * const contacts = Contact.createMany([contactData1, contactData2]);
183
+ * ```
98
184
  */
99
185
  static createMany(dataArray: ContactDTO[]): Contact[];
100
186
  /**
101
- * Creates a Contact instance from raw data without throwing
187
+ * Creates a Contact instance from raw data without throwing.
188
+ *
189
+ * @static
190
+ * @param {ContactDTO} data - Raw contact data
102
191
  * @returns {Contact | null} Contact instance or null if validation fails
192
+ *
193
+ * @example
194
+ * ```typescript
195
+ * const contact = Contact.createSafe(contactData);
196
+ * if (contact) {
197
+ * console.log("Contact created successfully");
198
+ * }
199
+ * ```
103
200
  */
104
201
  static createSafe(data: ContactDTO): Contact | null;
202
+ /**
203
+ * Creates a Contact instance from a JSON string.
204
+ *
205
+ * @static
206
+ * @param {string} jsonString - JSON string containing contact data
207
+ * @returns {Contact | undefined} Contact instance or undefined if parsing/validation fails
208
+ *
209
+ * @example
210
+ * ```typescript
211
+ * const jsonString = '{"id":"123","displayName":"John Doe",...}';
212
+ * const contact = Contact.fromJSON(jsonString);
213
+ * if (contact) {
214
+ * console.log(contact.displayName);
215
+ * }
216
+ * ```
217
+ */
218
+ static fromJSON(jsonString: string): Contact | undefined;
105
219
  /**
106
220
  * Checks if an unknown value contains valid data to construct a Contact instance.
107
221
  * This is useful when validating raw data structures before instantiation.
108
222
  *
223
+ * @static
109
224
  * @param {unknown} obj - The value containing potential contact data
110
225
  * @returns {obj is Contact} Type predicate indicating if a Contact can be constructed
111
226
  *
@@ -114,13 +229,10 @@ export declare class Contact {
114
229
  * const rawData = await fetchFromAPI();
115
230
  * if (Contact.canConstruct(rawData)) {
116
231
  * const contact = Contact.create(rawData);
117
- * // TypeScript knows contact is valid here
118
232
  * console.log(contact.displayName);
119
233
  * }
120
234
  * ```
121
235
  *
122
- * @throws {never} This method never throws errors
123
- *
124
236
  * @remarks
125
237
  * This method performs strict validation against the {@link ContactDTO} schema.
126
238
  */
@@ -129,6 +241,7 @@ export declare class Contact {
129
241
  * Validates if an unknown value is a Contact instance.
130
242
  * This is a runtime type guard that ensures proper object structure and data validity.
131
243
  *
244
+ * @static
132
245
  * @param {unknown} obj - The value to validate
133
246
  * @returns {obj is Contact} Type predicate indicating if the value is a valid Contact
134
247
  *
@@ -136,18 +249,15 @@ export declare class Contact {
136
249
  * ```typescript
137
250
  * const maybeContact = getContactFromCache();
138
251
  * if (Contact.is(maybeContact)) {
139
- * // TypeScript knows maybeContact is a Contact here
140
- * console.log(maybeContact.displayName);
252
+ * console.log(maybeContact.displayName); // Type-safe
141
253
  * }
142
254
  * ```
143
255
  *
144
- * @throws {never} This method never throws errors
145
- *
146
256
  * @remarks
147
257
  * This method performs a complete structural validation:
148
258
  * 1. Checks if the value is an object
149
259
  * 2. Verifies presence of internal data property
150
- * 3. Validates the data against ContactData schema
260
+ * 3. Validates the data against ContactDTO schema
151
261
  * 4. Ensures the object is a proper Contact instance
152
262
  *
153
263
  * Use this method when:
@@ -158,7 +268,29 @@ export declare class Contact {
158
268
  */
159
269
  static is(obj: unknown): obj is Contact;
160
270
  /**
161
- * Converts Payout instance to a plain object
271
+ * Converts the Contact instance to a plain object.
272
+ *
273
+ * @returns {ContactDTO} Plain object representation of the contact
274
+ *
275
+ * @example
276
+ * ```typescript
277
+ * const contact = Contact.from(contactData);
278
+ * const plainObject = contact?.toObject();
279
+ * // Use plainObject for API calls or storage
280
+ * ```
281
+ */
282
+ toObject(): ContactDTO;
283
+ /**
284
+ * Converts the Contact instance to a JSON string.
285
+ *
286
+ * @returns {string} JSON string representation of the contact
287
+ *
288
+ * @example
289
+ * ```typescript
290
+ * const contact = Contact.from(contactData);
291
+ * const jsonString = contact?.toJSON();
292
+ * localStorage.setItem('contact', jsonString);
293
+ * ```
162
294
  */
163
- toJSON(): ContactDTO;
295
+ toJSON(): string;
164
296
  }
@@ -0,0 +1,95 @@
1
+ import { QueryBuilder } from "@/lib/query/index.js";
2
+ import { WalletQueryDTO } from "./wallet.dtos.js";
3
+ /**
4
+ * Wallet-specific query builder that extends the base QueryBuilder
5
+ * and handles all possible input conversions (DTOs, URL params, etc.)
6
+ */
7
+ export declare class WalletQuery extends QueryBuilder {
8
+ /**
9
+ * Create empty wallet query with defaults
10
+ */
11
+ static create(): WalletQuery;
12
+ /**
13
+ * Create from typed DTO/filters object
14
+ */
15
+ static fromFilters(filters: WalletQueryDTO): WalletQuery;
16
+ /**
17
+ * Create from URL search parameters (strings)
18
+ */
19
+ static fromUrlParams(params: Record<string, string>): WalletQuery;
20
+ /**
21
+ * Create from URLSearchParams object
22
+ */
23
+ static fromSearchParams(searchParams: URLSearchParams): WalletQuery;
24
+ /**
25
+ * Create from Next.js Request object
26
+ */
27
+ static fromRequest(request: Request): WalletQuery;
28
+ /**
29
+ * Create from any supported input type
30
+ */
31
+ static from(input: QueryBuilder | WalletQueryDTO | Record<string, string> | URLSearchParams | null | undefined): WalletQuery;
32
+ /**
33
+ * Type guard for string records
34
+ */
35
+ private static isStringRecord;
36
+ whereId(id: string): this;
37
+ whereProfileId(profileId: string): this;
38
+ whereAccountNo(accountNo: string): this;
39
+ whereAccountName(accountName: string): this;
40
+ whereChannel(channel: string): this;
41
+ whereCountryCode(countryCode: string): this;
42
+ whereCurrencyCode(currencyCode: string): this;
43
+ /**
44
+ * Apply all filters from WalletQueryDTO object
45
+ */
46
+ private applyFilters;
47
+ /**
48
+ * Convert to WalletQueryDTO
49
+ */
50
+ toFilters(): WalletQueryDTO;
51
+ /**
52
+ * Convert to URL-safe string parameters
53
+ */
54
+ toUrlParams(): Record<string, string>;
55
+ /**
56
+ * Convert to URLSearchParams
57
+ */
58
+ toSearchParams(): URLSearchParams;
59
+ /**
60
+ * Convert to query string
61
+ */
62
+ toQueryString(): string;
63
+ /**
64
+ * Create new instance with wallet ID filter
65
+ */
66
+ withId(id: string): WalletQuery;
67
+ /**
68
+ * Create new instance with profile ID filter
69
+ */
70
+ withProfileId(profileId: string): WalletQuery;
71
+ /**
72
+ * Create new instance with account number filter
73
+ */
74
+ withAccountNo(accountNo: string): WalletQuery;
75
+ /**
76
+ * Create new instance with country code filter
77
+ */
78
+ withCountryCode(countryCode: string): WalletQuery;
79
+ /**
80
+ * Create new instance with currency code filter
81
+ */
82
+ withCurrencyCode(currencyCode: string): WalletQuery;
83
+ /**
84
+ * Check if any filters are applied
85
+ */
86
+ hasFilters(): boolean;
87
+ /**
88
+ * Get human-readable filter descriptions
89
+ */
90
+ getActiveFilters(): string[];
91
+ /**
92
+ * Extract filter values from QueryBuilder options
93
+ */
94
+ private extractFilterValues;
95
+ }
@@ -1,10 +1,14 @@
1
1
  import { Amount } from "@temboplus/frontend-core";
2
2
  import { WalletStatementEntry } from "./statement-entry.model.js";
3
3
  import { Wallet } from "./wallet.model.js";
4
- import { WalletDTOSchemas } from "./wallet.dtos.js";
5
- import { z } from "zod";
6
4
  import { contract } from "./wallet.contract.js";
7
5
  import { BaseRepository } from "@/lib/api/base-repository.js";
6
+ import { WalletQuery } from "./wallet.query.js";
7
+ import { WalletQueryDTO } from "./wallet.dtos.js";
8
+ /**
9
+ * Flexible query input type - supports the class, filters interface, URL params, etc.
10
+ */
11
+ export type WalletQueryInput = WalletQuery | WalletQueryDTO | Record<string, string> | URLSearchParams | null | undefined;
8
12
  /**
9
13
  * Repository class for managing wallet operations including balance checking,
10
14
  * statement generation, and wallet information retrieval.
@@ -87,13 +91,9 @@ export declare class WalletRepository extends BaseRepository<typeof contract> {
87
91
  /**
88
92
  * Retrieves all wallets associated with the authenticated user.
89
93
  *
90
- * Supports optional filtering through query parameters such as accountNo,
91
- * status, or currency filtering.
94
+ * Supports flexible filtering through WalletQuery or plain filter objects.
92
95
  *
93
96
  * @param query - Optional query parameters for filtering wallets
94
- * @param query.accountNo - Filter by specific account number
95
- * @param query.status - Filter by wallet status (active, inactive, etc.)
96
- * @param query.currencyCode - Filter by currency code
97
97
  * @returns Promise that resolves to an array of validated Wallet instances
98
98
  * @throws {Error} If the wallet fetch operation fails or data is invalid
99
99
  *
@@ -105,14 +105,38 @@ export declare class WalletRepository extends BaseRepository<typeof contract> {
105
105
  * // Get specific wallet by account number
106
106
  * const specificWallet = await repo.getWallets({ accountNo: '123456789' });
107
107
  *
108
- * // Get wallets with multiple filters
109
- * const activeUSDWallets = await repo.getWallets({
110
- * status: 'active',
111
- * currencyCode: 'USD'
112
- * });
108
+ * // Get wallets with multiple filters using WalletQuery
109
+ * const query = WalletQuery.create()
110
+ * .whereCountryCode('TZ')
111
+ * .whereCurrencyCode('TZS');
112
+ * const tzWallets = await repo.getWallets(query);
113
+ *
114
+ * // Get wallets with filters object
115
+ * const usdWallets = await repo.getWallets({ currencyCode: 'USD' });
113
116
  * ```
114
117
  */
115
- getWallets(query?: z.infer<typeof WalletDTOSchemas.walletQuery>): Promise<Wallet[]>;
118
+ getWallets(query?: WalletQueryInput): Promise<Wallet[]>;
119
+ /**
120
+ * Retrieves a single wallet by its ID.
121
+ *
122
+ * Since the API doesn't have a dedicated /id endpoint, this method queries
123
+ * the list endpoint with the ID filter and returns the first result.
124
+ *
125
+ * @param id - The unique identifier of the wallet to retrieve
126
+ * @returns Promise that resolves to the wallet if found, undefined otherwise
127
+ * @throws {Error} If the fetch operation fails
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * const wallet = await repo.getByID("wallet-id");
132
+ * if (wallet) {
133
+ * console.log(`Wallet: ${wallet.accountName}`);
134
+ * } else {
135
+ * console.log('Wallet not found');
136
+ * }
137
+ * ```
138
+ */
139
+ getByID(id: string): Promise<Wallet | undefined>;
116
140
  /**
117
141
  * Retrieves wallet statement entries for a specified date range.
118
142
  *
@@ -170,4 +194,12 @@ export declare class WalletRepository extends BaseRepository<typeof contract> {
170
194
  wallet?: Wallet;
171
195
  accountNo?: string;
172
196
  }): Promise<WalletStatementEntry[]>;
197
+ /**
198
+ * Check if a wallet exists with the given query
199
+ */
200
+ exists(query?: WalletQueryInput): Promise<boolean>;
201
+ /**
202
+ * Get count of wallets matching a query
203
+ */
204
+ count(query?: WalletQueryInput): Promise<number>;
173
205
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@temboplus/afloat",
3
- "version": "0.1.77-beta.6",
3
+ "version": "0.1.77-beta.8",
4
4
  "description": "A foundational library for Temboplus-Afloat projects.",
5
5
  "main": "./dist/index.cjs.js",
6
6
  "module": "./dist/index.esm.js",
@@ -58,6 +58,6 @@
58
58
  "typescript": "^5.8.3"
59
59
  },
60
60
  "dependencies": {
61
- "@temboplus/frontend-core": "^0.2.19"
61
+ "@temboplus/frontend-core": "^0.2.20-beta.0"
62
62
  }
63
63
  }