@webwaka/core 1.3.0

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.
Files changed (151) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/dist/ai.d.ts +25 -0
  3. package/dist/ai.d.ts.map +1 -0
  4. package/dist/ai.js +53 -0
  5. package/dist/ai.js.map +1 -0
  6. package/dist/core/ai/AIEngine.d.ts +69 -0
  7. package/dist/core/ai/AIEngine.d.ts.map +1 -0
  8. package/dist/core/ai/AIEngine.js +185 -0
  9. package/dist/core/ai/AIEngine.js.map +1 -0
  10. package/dist/core/auth/index.d.ts +183 -0
  11. package/dist/core/auth/index.d.ts.map +1 -0
  12. package/dist/core/auth/index.js +369 -0
  13. package/dist/core/auth/index.js.map +1 -0
  14. package/dist/core/billing/index.d.ts +52 -0
  15. package/dist/core/billing/index.d.ts.map +1 -0
  16. package/dist/core/billing/index.js +91 -0
  17. package/dist/core/billing/index.js.map +1 -0
  18. package/dist/core/booking/index.d.ts +38 -0
  19. package/dist/core/booking/index.d.ts.map +1 -0
  20. package/dist/core/booking/index.js +60 -0
  21. package/dist/core/booking/index.js.map +1 -0
  22. package/dist/core/chat/index.d.ts +48 -0
  23. package/dist/core/chat/index.d.ts.map +1 -0
  24. package/dist/core/chat/index.js +83 -0
  25. package/dist/core/chat/index.js.map +1 -0
  26. package/dist/core/document/index.d.ts +41 -0
  27. package/dist/core/document/index.d.ts.map +1 -0
  28. package/dist/core/document/index.js +68 -0
  29. package/dist/core/document/index.js.map +1 -0
  30. package/dist/core/events/index.d.ts +64 -0
  31. package/dist/core/events/index.d.ts.map +1 -0
  32. package/dist/core/events/index.js +60 -0
  33. package/dist/core/events/index.js.map +1 -0
  34. package/dist/core/geolocation/index.d.ts +32 -0
  35. package/dist/core/geolocation/index.d.ts.map +1 -0
  36. package/dist/core/geolocation/index.js +50 -0
  37. package/dist/core/geolocation/index.js.map +1 -0
  38. package/dist/core/kyc/index.d.ts +37 -0
  39. package/dist/core/kyc/index.d.ts.map +1 -0
  40. package/dist/core/kyc/index.js +60 -0
  41. package/dist/core/kyc/index.js.map +1 -0
  42. package/dist/core/logger/index.d.ts +60 -0
  43. package/dist/core/logger/index.d.ts.map +1 -0
  44. package/dist/core/logger/index.js +91 -0
  45. package/dist/core/logger/index.js.map +1 -0
  46. package/dist/core/notifications/index.d.ts +41 -0
  47. package/dist/core/notifications/index.d.ts.map +1 -0
  48. package/dist/core/notifications/index.js +111 -0
  49. package/dist/core/notifications/index.js.map +1 -0
  50. package/dist/core/rbac/index.d.ts +43 -0
  51. package/dist/core/rbac/index.d.ts.map +1 -0
  52. package/dist/core/rbac/index.js +66 -0
  53. package/dist/core/rbac/index.js.map +1 -0
  54. package/dist/events.d.ts +23 -0
  55. package/dist/events.d.ts.map +1 -0
  56. package/dist/events.js +22 -0
  57. package/dist/events.js.map +1 -0
  58. package/dist/index.d.ts +33 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +56 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/kyc.d.ts +12 -0
  63. package/dist/kyc.d.ts.map +1 -0
  64. package/dist/kyc.js +2 -0
  65. package/dist/kyc.js.map +1 -0
  66. package/dist/nanoid.d.ts +8 -0
  67. package/dist/nanoid.d.ts.map +1 -0
  68. package/dist/nanoid.js +15 -0
  69. package/dist/nanoid.js.map +1 -0
  70. package/dist/ndpr.d.ts +13 -0
  71. package/dist/ndpr.d.ts.map +1 -0
  72. package/dist/ndpr.js +19 -0
  73. package/dist/ndpr.js.map +1 -0
  74. package/dist/optimistic-lock.d.ts +11 -0
  75. package/dist/optimistic-lock.d.ts.map +1 -0
  76. package/dist/optimistic-lock.js +24 -0
  77. package/dist/optimistic-lock.js.map +1 -0
  78. package/dist/payment.d.ts +41 -0
  79. package/dist/payment.d.ts.map +1 -0
  80. package/dist/payment.js +116 -0
  81. package/dist/payment.js.map +1 -0
  82. package/dist/pin.d.ts +6 -0
  83. package/dist/pin.d.ts.map +1 -0
  84. package/dist/pin.js +18 -0
  85. package/dist/pin.js.map +1 -0
  86. package/dist/query-helpers.d.ts +18 -0
  87. package/dist/query-helpers.d.ts.map +1 -0
  88. package/dist/query-helpers.js +22 -0
  89. package/dist/query-helpers.js.map +1 -0
  90. package/dist/rate-limit.d.ts +13 -0
  91. package/dist/rate-limit.d.ts.map +1 -0
  92. package/dist/rate-limit.js +16 -0
  93. package/dist/rate-limit.js.map +1 -0
  94. package/dist/sms.d.ts +23 -0
  95. package/dist/sms.d.ts.map +1 -0
  96. package/dist/sms.js +60 -0
  97. package/dist/sms.js.map +1 -0
  98. package/dist/tax.d.ts +25 -0
  99. package/dist/tax.d.ts.map +1 -0
  100. package/dist/tax.js +31 -0
  101. package/dist/tax.js.map +1 -0
  102. package/package.json +99 -0
  103. package/src/ai.test.ts +146 -0
  104. package/src/ai.ts +75 -0
  105. package/src/core/ai/AIEngine.test.ts +386 -0
  106. package/src/core/ai/AIEngine.ts +281 -0
  107. package/src/core/auth/index.test.ts +268 -0
  108. package/src/core/auth/index.ts +570 -0
  109. package/src/core/billing/index.test.ts +154 -0
  110. package/src/core/billing/index.ts +132 -0
  111. package/src/core/booking/index.test.ts +153 -0
  112. package/src/core/booking/index.ts +91 -0
  113. package/src/core/chat/index.test.ts +159 -0
  114. package/src/core/chat/index.ts +130 -0
  115. package/src/core/document/index.test.ts +106 -0
  116. package/src/core/document/index.ts +99 -0
  117. package/src/core/events/index.test.ts +91 -0
  118. package/src/core/events/index.ts +91 -0
  119. package/src/core/geolocation/index.test.ts +70 -0
  120. package/src/core/geolocation/index.ts +69 -0
  121. package/src/core/kyc/index.test.ts +105 -0
  122. package/src/core/kyc/index.ts +86 -0
  123. package/src/core/logger/index.test.ts +110 -0
  124. package/src/core/logger/index.ts +127 -0
  125. package/src/core/notifications/index.test.ts +85 -0
  126. package/src/core/notifications/index.ts +136 -0
  127. package/src/core/rbac/index.test.ts +81 -0
  128. package/src/core/rbac/index.ts +85 -0
  129. package/src/events.test.ts +43 -0
  130. package/src/events.ts +23 -0
  131. package/src/index.test.ts +123 -0
  132. package/src/index.ts +97 -0
  133. package/src/kyc.ts +23 -0
  134. package/src/nanoid.test.ts +43 -0
  135. package/src/nanoid.ts +16 -0
  136. package/src/ndpr.test.ts +68 -0
  137. package/src/ndpr.ts +49 -0
  138. package/src/optimistic-lock.test.ts +75 -0
  139. package/src/optimistic-lock.ts +36 -0
  140. package/src/payment.test.ts +152 -0
  141. package/src/payment.ts +163 -0
  142. package/src/pin.test.ts +57 -0
  143. package/src/pin.ts +38 -0
  144. package/src/query-helpers.test.ts +98 -0
  145. package/src/query-helpers.ts +36 -0
  146. package/src/rate-limit.test.ts +98 -0
  147. package/src/rate-limit.ts +33 -0
  148. package/src/sms.test.ts +112 -0
  149. package/src/sms.ts +85 -0
  150. package/src/tax.test.ts +85 -0
  151. package/src/tax.ts +57 -0
package/dist/nanoid.js ADDED
@@ -0,0 +1,15 @@
1
+ const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
2
+ /**
3
+ * Cloudflare Worker-compatible nanoid. Uses crypto.getRandomValues.
4
+ * prefix is prepended to the ID. Default length 21.
5
+ */
6
+ export function nanoid(prefix = '', length = 21) {
7
+ const bytes = crypto.getRandomValues(new Uint8Array(length));
8
+ const id = Array.from(bytes)
9
+ .map((b) => CHARS[b % CHARS.length])
10
+ .join('');
11
+ return prefix ? `${prefix}_${id}` : id;
12
+ }
13
+ /** Alias for nanoid — allows repos to migrate without breaking during transition. */
14
+ export const genId = nanoid;
15
+ //# sourceMappingURL=nanoid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nanoid.js","sourceRoot":"","sources":["../src/nanoid.ts"],"names":[],"mappings":"AAAA,MAAM,KAAK,GAAG,gEAAgE,CAAC;AAE/E;;;GAGG;AACH,MAAM,UAAU,MAAM,CAAC,MAAM,GAAG,EAAE,EAAE,MAAM,GAAG,EAAE;IAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7D,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;SACzB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;SACnC,IAAI,CAAC,EAAE,CAAC,CAAC;IACZ,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACzC,CAAC;AAED,qFAAqF;AACrF,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC"}
package/dist/ndpr.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import type { D1Database } from '@cloudflare/workers-types';
2
+ export interface NdprConsentLog {
3
+ id: string;
4
+ entity_id: string;
5
+ entity_type: string;
6
+ consented_at: number;
7
+ ip_address: string | null;
8
+ user_agent: string | null;
9
+ created_at: number;
10
+ }
11
+ export declare function assertNdprConsent(body: unknown): void;
12
+ export declare function recordNdprConsent(db: D1Database, entityId: string, entityType: string, ipAddress: string | null, userAgent: string | null): Promise<void>;
13
+ //# sourceMappingURL=ndpr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ndpr.d.ts","sourceRoot":"","sources":["../src/ndpr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAWrD;AAED,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,UAAU,EACd,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,SAAS,EAAE,MAAM,GAAG,IAAI,GACvB,OAAO,CAAC,IAAI,CAAC,CAiBf"}
package/dist/ndpr.js ADDED
@@ -0,0 +1,19 @@
1
+ export function assertNdprConsent(body) {
2
+ if (typeof body !== 'object' ||
3
+ body === null ||
4
+ body.ndpr_consent !== true) {
5
+ const err = new Error('NDPR consent is required');
6
+ err.status = 400;
7
+ err.code = 'NDPR_CONSENT_REQUIRED';
8
+ throw err;
9
+ }
10
+ }
11
+ export async function recordNdprConsent(db, entityId, entityType, ipAddress, userAgent) {
12
+ const now = Date.now();
13
+ await db
14
+ .prepare(`INSERT OR IGNORE INTO ndpr_consent_log (id, entity_id, entity_type, consented_at, ip_address, user_agent, created_at)
15
+ VALUES (?, ?, ?, ?, ?, ?, ?)`)
16
+ .bind(`ndpr_${now}_${Array.from(crypto.getRandomValues(new Uint8Array(4))).map(b => b.toString(16).padStart(2, '0')).join('')}`, entityId, entityType, now, ipAddress, userAgent, now)
17
+ .run();
18
+ }
19
+ //# sourceMappingURL=ndpr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ndpr.js","sourceRoot":"","sources":["../src/ndpr.ts"],"names":[],"mappings":"AAYA,MAAM,UAAU,iBAAiB,CAAC,IAAa;IAC7C,IACE,OAAO,IAAI,KAAK,QAAQ;QACxB,IAAI,KAAK,IAAI;QACZ,IAAgC,CAAC,YAAY,KAAK,IAAI,EACvD,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QACjD,GAAW,CAAC,MAAM,GAAG,GAAG,CAAC;QACzB,GAAW,CAAC,IAAI,GAAG,uBAAuB,CAAC;QAC5C,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,EAAc,EACd,QAAgB,EAChB,UAAkB,EAClB,SAAwB,EACxB,SAAwB;IAExB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,EAAE;SACL,OAAO,CACN;oCAC8B,CAC/B;SACA,IAAI,CACH,QAAQ,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EACzH,QAAQ,EACR,UAAU,EACV,GAAG,EACH,SAAS,EACT,SAAS,EACT,GAAG,CACJ;SACA,GAAG,EAAE,CAAC;AACX,CAAC"}
@@ -0,0 +1,11 @@
1
+ export interface OptimisticLockResult {
2
+ success: boolean;
3
+ conflict: boolean;
4
+ error?: string;
5
+ }
6
+ export declare function updateWithVersionLock(db: D1Database, table: string, updates: Record<string, any>, where: {
7
+ id: string;
8
+ tenantId: string;
9
+ expectedVersion: number;
10
+ }): Promise<OptimisticLockResult>;
11
+ //# sourceMappingURL=optimistic-lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optimistic-lock.d.ts","sourceRoot":"","sources":["../src/optimistic-lock.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,qBAAqB,CACzC,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5B,KAAK,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,GAC/D,OAAO,CAAC,oBAAoB,CAAC,CAwB/B"}
@@ -0,0 +1,24 @@
1
+ export async function updateWithVersionLock(db, table, updates, where) {
2
+ try {
3
+ const now = new Date().toISOString();
4
+ const setClauses = Object.keys(updates)
5
+ .map((col) => `${col} = ?`)
6
+ .join(', ');
7
+ const setValues = Object.values(updates);
8
+ const sql = `
9
+ UPDATE ${table}
10
+ SET ${setClauses}, version = version + 1, updatedAt = ?
11
+ WHERE id = ? AND tenantId = ? AND version = ? AND deletedAt IS NULL
12
+ `;
13
+ const stmt = db.prepare(sql).bind(...setValues, now, where.id, where.tenantId, where.expectedVersion);
14
+ const result = await stmt.run();
15
+ if (result.meta.changes === 0) {
16
+ return { success: false, conflict: true };
17
+ }
18
+ return { success: true, conflict: false };
19
+ }
20
+ catch (err) {
21
+ return { success: false, conflict: false, error: err?.message };
22
+ }
23
+ }
24
+ //# sourceMappingURL=optimistic-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optimistic-lock.js","sourceRoot":"","sources":["../src/optimistic-lock.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,EAAc,EACd,KAAa,EACb,OAA4B,EAC5B,KAAgE;IAEhE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;aACpC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC;aAC1B,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAEzC,MAAM,GAAG,GAAG;eACD,KAAK;YACR,UAAU;;KAEjB,CAAC;QAEF,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,SAAS,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;QACtG,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QAEhC,IAAK,MAAM,CAAC,IAAY,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;YACvC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC5C,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAC5C,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;IAClE,CAAC;AACH,CAAC"}
@@ -0,0 +1,41 @@
1
+ export interface ChargeResult {
2
+ success: boolean;
3
+ reference: string;
4
+ amountKobo: number;
5
+ error?: string;
6
+ }
7
+ export interface RefundResult {
8
+ success: boolean;
9
+ refundId: string;
10
+ error?: string;
11
+ }
12
+ export interface SplitRecipient {
13
+ subaccountCode: string;
14
+ amountKobo: number;
15
+ }
16
+ export interface IPaymentProvider {
17
+ verifyCharge(reference: string): Promise<ChargeResult>;
18
+ initiateRefund(reference: string, amountKobo?: number): Promise<RefundResult>;
19
+ initiateSplit(totalKobo: number, recipients: SplitRecipient[], reference: string): Promise<ChargeResult>;
20
+ initiateTransfer(recipientCode: string, amountKobo: number, reference: string): Promise<{
21
+ success: boolean;
22
+ transferCode: string;
23
+ error?: string;
24
+ }>;
25
+ }
26
+ export declare class PaystackProvider implements IPaymentProvider {
27
+ private secretKey;
28
+ private baseUrl;
29
+ constructor(secretKey: string);
30
+ private get headers();
31
+ verifyCharge(reference: string): Promise<ChargeResult>;
32
+ initiateRefund(reference: string, amountKobo?: number): Promise<RefundResult>;
33
+ initiateSplit(totalKobo: number, recipients: SplitRecipient[], reference: string): Promise<ChargeResult>;
34
+ initiateTransfer(recipientCode: string, amountKobo: number, reference: string): Promise<{
35
+ success: boolean;
36
+ transferCode: string;
37
+ error?: string;
38
+ }>;
39
+ }
40
+ export declare function createPaymentProvider(secretKey: string): IPaymentProvider;
41
+ //# sourceMappingURL=payment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payment.d.ts","sourceRoot":"","sources":["../src/payment.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACvD,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC9E,aAAa,CACX,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,cAAc,EAAE,EAC5B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,YAAY,CAAC,CAAC;IACzB,gBAAgB,CACd,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxE;AAED,qBAAa,gBAAiB,YAAW,gBAAgB;IACvD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAA6B;gBAEhC,SAAS,EAAE,MAAM;IAI7B,OAAO,KAAK,OAAO,GAKlB;IAEK,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IA0BtD,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAoB7E,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,cAAc,EAAE,EAC5B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,YAAY,CAAC;IA+BlB,gBAAgB,CACpB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAyBvE;AAED,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,CAEzE"}
@@ -0,0 +1,116 @@
1
+ export class PaystackProvider {
2
+ secretKey;
3
+ baseUrl = 'https://api.paystack.co';
4
+ constructor(secretKey) {
5
+ this.secretKey = secretKey;
6
+ }
7
+ get headers() {
8
+ return {
9
+ Authorization: `Bearer ${this.secretKey}`,
10
+ 'Content-Type': 'application/json',
11
+ };
12
+ }
13
+ async verifyCharge(reference) {
14
+ try {
15
+ const res = await fetch(`${this.baseUrl}/transaction/verify/${encodeURIComponent(reference)}`, { method: 'GET', headers: this.headers });
16
+ const data = (await res.json());
17
+ if (!res.ok || !data.status) {
18
+ return {
19
+ success: false,
20
+ reference,
21
+ amountKobo: 0,
22
+ error: data.message ?? 'Verification failed',
23
+ };
24
+ }
25
+ return {
26
+ success: data.data?.status === 'success',
27
+ reference,
28
+ amountKobo: data.data?.amount ?? 0,
29
+ error: data.data?.status !== 'success' ? data.data?.gateway_response : undefined,
30
+ };
31
+ }
32
+ catch (err) {
33
+ return { success: false, reference, amountKobo: 0, error: err?.message };
34
+ }
35
+ }
36
+ async initiateRefund(reference, amountKobo) {
37
+ try {
38
+ const body = { transaction: reference };
39
+ if (amountKobo !== undefined)
40
+ body.amount = amountKobo;
41
+ const res = await fetch(`${this.baseUrl}/refund`, {
42
+ method: 'POST',
43
+ headers: this.headers,
44
+ body: JSON.stringify(body),
45
+ });
46
+ const data = (await res.json());
47
+ if (!res.ok || !data.status) {
48
+ return { success: false, refundId: '', error: data.message ?? 'Refund failed' };
49
+ }
50
+ return { success: true, refundId: String(data.data?.id ?? '') };
51
+ }
52
+ catch (err) {
53
+ return { success: false, refundId: '', error: err?.message };
54
+ }
55
+ }
56
+ async initiateSplit(totalKobo, recipients, reference) {
57
+ const subaccounts = recipients.map((r) => ({
58
+ subaccount: r.subaccountCode,
59
+ amount: r.amountKobo,
60
+ }));
61
+ try {
62
+ const res = await fetch(`${this.baseUrl}/transaction/initialize`, {
63
+ method: 'POST',
64
+ headers: this.headers,
65
+ body: JSON.stringify({
66
+ amount: totalKobo,
67
+ reference,
68
+ split: { type: 'flat', subaccounts },
69
+ }),
70
+ });
71
+ const data = (await res.json());
72
+ if (!res.ok || !data.status) {
73
+ return {
74
+ success: false,
75
+ reference,
76
+ amountKobo: totalKobo,
77
+ error: data.message ?? 'Split initiation failed',
78
+ };
79
+ }
80
+ return { success: true, reference, amountKobo: totalKobo };
81
+ }
82
+ catch (err) {
83
+ return { success: false, reference, amountKobo: totalKobo, error: err?.message };
84
+ }
85
+ }
86
+ async initiateTransfer(recipientCode, amountKobo, reference) {
87
+ try {
88
+ const res = await fetch(`${this.baseUrl}/transfer`, {
89
+ method: 'POST',
90
+ headers: this.headers,
91
+ body: JSON.stringify({
92
+ source: 'balance',
93
+ amount: amountKobo,
94
+ recipient: recipientCode,
95
+ reference,
96
+ }),
97
+ });
98
+ const data = (await res.json());
99
+ if (!res.ok || !data.status) {
100
+ return {
101
+ success: false,
102
+ transferCode: '',
103
+ error: data.message ?? 'Transfer failed',
104
+ };
105
+ }
106
+ return { success: true, transferCode: data.data?.transfer_code ?? '' };
107
+ }
108
+ catch (err) {
109
+ return { success: false, transferCode: '', error: err?.message };
110
+ }
111
+ }
112
+ }
113
+ export function createPaymentProvider(secretKey) {
114
+ return new PaystackProvider(secretKey);
115
+ }
116
+ //# sourceMappingURL=payment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payment.js","sourceRoot":"","sources":["../src/payment.ts"],"names":[],"mappings":"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"}
package/dist/pin.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export declare function hashPin(pin: string, salt?: string): Promise<{
2
+ hash: string;
3
+ salt: string;
4
+ }>;
5
+ export declare function verifyPin(pin: string, storedHash: string, salt: string): Promise<boolean>;
6
+ //# sourceMappingURL=pin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pin.d.ts","sourceRoot":"","sources":["../src/pin.ts"],"names":[],"mappings":"AAAA,wBAAsB,OAAO,CAC3B,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAyBzC;AAED,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,OAAO,CAAC,CAGlB"}
package/dist/pin.js ADDED
@@ -0,0 +1,18 @@
1
+ export async function hashPin(pin, salt) {
2
+ const usedSalt = salt ?? crypto.randomUUID();
3
+ const enc = new TextEncoder();
4
+ const keyMaterial = await crypto.subtle.importKey('raw', enc.encode(pin), 'PBKDF2', false, ['deriveBits']);
5
+ const bits = await crypto.subtle.deriveBits({
6
+ name: 'PBKDF2',
7
+ salt: enc.encode(usedSalt),
8
+ iterations: 100_000,
9
+ hash: 'SHA-256',
10
+ }, keyMaterial, 256);
11
+ const hash = btoa(String.fromCharCode(...new Uint8Array(bits)));
12
+ return { hash, salt: usedSalt };
13
+ }
14
+ export async function verifyPin(pin, storedHash, salt) {
15
+ const { hash } = await hashPin(pin, salt);
16
+ return hash === storedHash;
17
+ }
18
+ //# sourceMappingURL=pin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pin.js","sourceRoot":"","sources":["../src/pin.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,GAAW,EACX,IAAa;IAEb,MAAM,QAAQ,GAAG,IAAI,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;IAC7C,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC;IAE9B,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAC/C,KAAK,EACL,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,EACf,QAAQ,EACR,KAAK,EACL,CAAC,YAAY,CAAC,CACf,CAAC;IAEF,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CACzC;QACE,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC;QAC1B,UAAU,EAAE,OAAO;QACnB,IAAI,EAAE,SAAS;KAChB,EACD,WAAW,EACX,GAAG,CACJ,CAAC;IAEF,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAChE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAClC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAW,EACX,UAAkB,EAClB,IAAY;IAEZ,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC1C,OAAO,IAAI,KAAK,UAAU,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,18 @@
1
+ export declare function parsePagination(q: Record<string, string>, maxLimit?: number): {
2
+ limit: number;
3
+ offset: number;
4
+ };
5
+ export declare function metaResponse<T>(data: T[], total: number, limit: number, offset: number): {
6
+ data: T[];
7
+ meta: {
8
+ total: number;
9
+ limit: number;
10
+ offset: number;
11
+ has_more: boolean;
12
+ };
13
+ };
14
+ export declare function applyTenantScope(baseQuery: string, params: unknown[], tenantId: string, column?: string): {
15
+ query: string;
16
+ params: unknown[];
17
+ };
18
+ //# sourceMappingURL=query-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-helpers.d.ts","sourceRoot":"","sources":["../src/query-helpers.ts"],"names":[],"mappings":"AAAA,wBAAgB,eAAe,CAC7B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACzB,QAAQ,SAAM,GACb;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAInC;AAED,wBAAgB,YAAY,CAAC,CAAC,EAC5B,IAAI,EAAE,CAAC,EAAE,EACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM;;;;;;;;EAWf;AAED,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,OAAO,EAAE,EACjB,QAAQ,EAAE,MAAM,EAChB,MAAM,SAAgB,GACrB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,EAAE,CAAA;CAAE,CAItC"}
@@ -0,0 +1,22 @@
1
+ export function parsePagination(q, maxLimit = 100) {
2
+ const limit = Math.min(Math.max(parseInt(q.limit ?? '20', 10) || 20, 1), maxLimit);
3
+ const offset = Math.max(parseInt(q.offset ?? '0', 10) || 0, 0);
4
+ return { limit, offset };
5
+ }
6
+ export function metaResponse(data, total, limit, offset) {
7
+ return {
8
+ data,
9
+ meta: {
10
+ total,
11
+ limit,
12
+ offset,
13
+ has_more: offset + limit < total,
14
+ },
15
+ };
16
+ }
17
+ export function applyTenantScope(baseQuery, params, tenantId, column = 'operator_id') {
18
+ const hasWhere = /\bWHERE\b/i.test(baseQuery);
19
+ const clause = hasWhere ? ` AND ${column} = ?` : ` WHERE ${column} = ?`;
20
+ return { query: baseQuery + clause, params: [...params, tenantId] };
21
+ }
22
+ //# sourceMappingURL=query-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-helpers.js","sourceRoot":"","sources":["../src/query-helpers.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,eAAe,CAC7B,CAAyB,EACzB,QAAQ,GAAG,GAAG;IAEd,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IACnF,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,IAAI,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/D,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,IAAS,EACT,KAAa,EACb,KAAa,EACb,MAAc;IAEd,OAAO;QACL,IAAI;QACJ,IAAI,EAAE;YACJ,KAAK;YACL,KAAK;YACL,MAAM;YACN,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG,KAAK;SACjC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,SAAiB,EACjB,MAAiB,EACjB,QAAgB,EAChB,MAAM,GAAG,aAAa;IAEtB,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,MAAM,MAAM,CAAC,CAAC,CAAC,UAAU,MAAM,MAAM,CAAC;IACxE,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;AACtE,CAAC"}
@@ -0,0 +1,13 @@
1
+ export interface RateLimitOptions {
2
+ kv: KVNamespace;
3
+ key: string;
4
+ maxRequests: number;
5
+ windowSeconds: number;
6
+ }
7
+ export interface RateLimitResult {
8
+ allowed: boolean;
9
+ remaining: number;
10
+ resetAt: number;
11
+ }
12
+ export declare function checkRateLimit(opts: RateLimitOptions): Promise<RateLimitResult>;
13
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,WAAW,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAmBrF"}
@@ -0,0 +1,16 @@
1
+ export async function checkRateLimit(opts) {
2
+ const { kv, key, maxRequests, windowSeconds } = opts;
3
+ const nowSeconds = Math.floor(Date.now() / 1000);
4
+ const windowStart = Math.floor(nowSeconds / windowSeconds) * windowSeconds;
5
+ const resetAt = (windowStart + windowSeconds) * 1000;
6
+ const kvKey = `rl:${key}:${windowStart}`;
7
+ const raw = await kv.get(kvKey);
8
+ let count = raw ? parseInt(raw, 10) : 0;
9
+ if (count >= maxRequests) {
10
+ return { allowed: false, remaining: 0, resetAt };
11
+ }
12
+ count += 1;
13
+ await kv.put(kvKey, String(count), { expirationTtl: windowSeconds * 2 });
14
+ return { allowed: true, remaining: maxRequests - count, resetAt };
15
+ }
16
+ //# sourceMappingURL=rate-limit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAaA,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAsB;IACzD,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC;IAErD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IACjD,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,aAAa,CAAC,GAAG,aAAa,CAAC;IAC3E,MAAM,OAAO,GAAG,CAAC,WAAW,GAAG,aAAa,CAAC,GAAG,IAAI,CAAC;IACrD,MAAM,KAAK,GAAG,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;IAEzC,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAChC,IAAI,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAExC,IAAI,KAAK,IAAI,WAAW,EAAE,CAAC;QACzB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC;IACnD,CAAC;IAED,KAAK,IAAI,CAAC,CAAC;IACX,MAAM,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,aAAa,EAAE,aAAa,GAAG,CAAC,EAAE,CAAC,CAAC;IAEzE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,GAAG,KAAK,EAAE,OAAO,EAAE,CAAC;AACpE,CAAC"}
package/dist/sms.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ export type OtpChannel = 'sms' | 'whatsapp' | 'whatsapp_business';
2
+ export interface OtpResult {
3
+ success: boolean;
4
+ messageId?: string;
5
+ channel: OtpChannel;
6
+ error?: string;
7
+ }
8
+ export interface ISmsProvider {
9
+ sendOtp(to: string, message: string, channel?: OtpChannel): Promise<OtpResult>;
10
+ sendMessage(to: string, message: string): Promise<OtpResult>;
11
+ }
12
+ export declare class TermiiProvider implements ISmsProvider {
13
+ private apiKey;
14
+ private senderId;
15
+ private apiUrl;
16
+ constructor(apiKey: string, senderId?: string);
17
+ sendOtp(to: string, message: string, channel?: OtpChannel): Promise<OtpResult>;
18
+ sendMessage(to: string, message: string): Promise<OtpResult>;
19
+ private deliver;
20
+ }
21
+ export declare function createSmsProvider(apiKey: string, senderId?: string): ISmsProvider;
22
+ export declare function sendTermiiSms(to: string, message: string, apiKey: string, senderId?: string): Promise<OtpResult>;
23
+ //# sourceMappingURL=sms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sms.d.ts","sourceRoot":"","sources":["../src/sms.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,UAAU,GAAG,mBAAmB,CAAC;AAElE,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,UAAU,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAC/E,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;CAC9D;AAED,qBAAa,cAAe,YAAW,YAAY;IACjD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAA4C;gBAE9C,MAAM,EAAE,MAAM,EAAE,QAAQ,SAAY;IAK1C,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,UAAuB,GAAG,OAAO,CAAC,SAAS,CAAC;IAQ1F,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;YAIpD,OAAO;CAkCtB;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,YAAY,CAEjF;AAED,wBAAsB,aAAa,CACjC,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,SAAS,CAAC,CAGpB"}
package/dist/sms.js ADDED
@@ -0,0 +1,60 @@
1
+ export class TermiiProvider {
2
+ apiKey;
3
+ senderId;
4
+ apiUrl = 'https://api.ng.termii.com/api/sms/send';
5
+ constructor(apiKey, senderId = 'WebWaka') {
6
+ this.apiKey = apiKey;
7
+ this.senderId = senderId;
8
+ }
9
+ async sendOtp(to, message, channel = 'whatsapp') {
10
+ const result = await this.deliver(to, message, channel);
11
+ if (!result.success && channel !== 'sms') {
12
+ return this.deliver(to, message, 'sms');
13
+ }
14
+ return result;
15
+ }
16
+ async sendMessage(to, message) {
17
+ return this.sendOtp(to, message, 'whatsapp');
18
+ }
19
+ async deliver(to, message, channel) {
20
+ const termiiChannel = channel === 'whatsapp' || channel === 'whatsapp_business' ? 'whatsapp' : 'generic';
21
+ try {
22
+ const res = await fetch(this.apiUrl, {
23
+ method: 'POST',
24
+ headers: { 'Content-Type': 'application/json' },
25
+ body: JSON.stringify({
26
+ to,
27
+ from: this.senderId,
28
+ sms: message,
29
+ type: 'plain',
30
+ channel: termiiChannel,
31
+ api_key: this.apiKey,
32
+ }),
33
+ });
34
+ const data = (await res.json());
35
+ if (!res.ok) {
36
+ return {
37
+ success: false,
38
+ channel,
39
+ error: data?.message ?? 'Termii request failed',
40
+ };
41
+ }
42
+ return {
43
+ success: true,
44
+ messageId: data?.message_id ?? data?.pinId,
45
+ channel,
46
+ };
47
+ }
48
+ catch (err) {
49
+ return { success: false, channel, error: err?.message };
50
+ }
51
+ }
52
+ }
53
+ export function createSmsProvider(apiKey, senderId) {
54
+ return new TermiiProvider(apiKey, senderId);
55
+ }
56
+ export async function sendTermiiSms(to, message, apiKey, senderId) {
57
+ const provider = createSmsProvider(apiKey, senderId);
58
+ return provider.sendMessage(to, message);
59
+ }
60
+ //# sourceMappingURL=sms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sms.js","sourceRoot":"","sources":["../src/sms.ts"],"names":[],"mappings":"AAcA,MAAM,OAAO,cAAc;IACjB,MAAM,CAAS;IACf,QAAQ,CAAS;IACjB,MAAM,GAAG,wCAAwC,CAAC;IAE1D,YAAY,MAAc,EAAE,QAAQ,GAAG,SAAS;QAC9C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,OAAe,EAAE,UAAsB,UAAU;QACzE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;YACzC,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,EAAU,EAAE,OAAe;QAC3C,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;IAC/C,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,OAAe,EAAE,OAAmB;QACpE,MAAM,aAAa,GACjB,OAAO,KAAK,UAAU,IAAI,OAAO,KAAK,mBAAmB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;QAErF,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE;gBACnC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,EAAE;oBACF,IAAI,EAAE,IAAI,CAAC,QAAQ;oBACnB,GAAG,EAAE,OAAO;oBACZ,IAAI,EAAE,OAAO;oBACb,OAAO,EAAE,aAAa;oBACtB,OAAO,EAAE,IAAI,CAAC,MAAM;iBACrB,CAAC;aACH,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAQ,CAAC;YACvC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,OAAO;oBACL,OAAO,EAAE,KAAK;oBACd,OAAO;oBACP,KAAK,EAAE,IAAI,EAAE,OAAO,IAAI,uBAAuB;iBAChD,CAAC;YACJ,CAAC;YACD,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,SAAS,EAAE,IAAI,EAAE,UAAU,IAAI,IAAI,EAAE,KAAK;gBAC1C,OAAO;aACR,CAAC;QACJ,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;QAC1D,CAAC;IACH,CAAC;CACF;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAc,EAAE,QAAiB;IACjE,OAAO,IAAI,cAAc,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,EAAU,EACV,OAAe,EACf,MAAc,EACd,QAAiB;IAEjB,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACrD,OAAO,QAAQ,CAAC,WAAW,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC"}
package/dist/tax.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ export interface TaxConfig {
2
+ vatRate: number;
3
+ vatRegistered: boolean;
4
+ exemptCategories: string[];
5
+ }
6
+ export interface TaxLineItem {
7
+ category: string;
8
+ amountKobo: number;
9
+ }
10
+ export interface TaxResult {
11
+ subtotalKobo: number;
12
+ vatKobo: number;
13
+ totalKobo: number;
14
+ vatBreakdown: {
15
+ category: string;
16
+ vatKobo: number;
17
+ }[];
18
+ }
19
+ export declare class TaxEngine {
20
+ private config;
21
+ constructor(config: TaxConfig);
22
+ compute(items: TaxLineItem[]): TaxResult;
23
+ }
24
+ export declare function createTaxEngine(config: TaxConfig): TaxEngine;
25
+ //# sourceMappingURL=tax.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tax.d.ts","sourceRoot":"","sources":["../src/tax.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACvD;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAY;gBAEd,MAAM,EAAE,SAAS;IAI7B,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,SAAS;CA2BzC;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,CAE5D"}
package/dist/tax.js ADDED
@@ -0,0 +1,31 @@
1
+ export class TaxEngine {
2
+ config;
3
+ constructor(config) {
4
+ this.config = config;
5
+ }
6
+ compute(items) {
7
+ let subtotalKobo = 0;
8
+ let vatKobo = 0;
9
+ const vatBreakdown = [];
10
+ for (const item of items) {
11
+ subtotalKobo += item.amountKobo;
12
+ const isExempt = !this.config.vatRegistered ||
13
+ this.config.exemptCategories.includes(item.category);
14
+ const itemVat = isExempt
15
+ ? 0
16
+ : Math.round(item.amountKobo * this.config.vatRate);
17
+ vatKobo += itemVat;
18
+ vatBreakdown.push({ category: item.category, vatKobo: itemVat });
19
+ }
20
+ return {
21
+ subtotalKobo,
22
+ vatKobo,
23
+ totalKobo: subtotalKobo + vatKobo,
24
+ vatBreakdown,
25
+ };
26
+ }
27
+ }
28
+ export function createTaxEngine(config) {
29
+ return new TaxEngine(config);
30
+ }
31
+ //# sourceMappingURL=tax.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tax.js","sourceRoot":"","sources":["../src/tax.ts"],"names":[],"mappings":"AAkBA,MAAM,OAAO,SAAS;IACZ,MAAM,CAAY;IAE1B,YAAY,MAAiB;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,OAAO,CAAC,KAAoB;QAC1B,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,MAAM,YAAY,GAA4C,EAAE,CAAC;QAEjE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,YAAY,IAAI,IAAI,CAAC,UAAU,CAAC;YAEhC,MAAM,QAAQ,GACZ,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa;gBAC1B,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAEvD,MAAM,OAAO,GAAG,QAAQ;gBACtB,CAAC,CAAC,CAAC;gBACH,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAEtD,OAAO,IAAI,OAAO,CAAC;YACnB,YAAY,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QACnE,CAAC;QAED,OAAO;YACL,YAAY;YACZ,OAAO;YACP,SAAS,EAAE,YAAY,GAAG,OAAO;YACjC,YAAY;SACb,CAAC;IACJ,CAAC;CACF;AAED,MAAM,UAAU,eAAe,CAAC,MAAiB;IAC/C,OAAO,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC"}