joopjs 2.0.5 → 2.1.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 (146) hide show
  1. package/.claude/skills/auth.md +235 -0
  2. package/.claude/skills/banking.md +377 -0
  3. package/.claude/skills/encryption.md +265 -0
  4. package/.claude/skills/finance.md +248 -0
  5. package/.claude/skills/observables.md +270 -0
  6. package/.claude/skills/security.md +240 -0
  7. package/.claude/skills/setup.md +196 -0
  8. package/.cursor/rules/joopjs.mdc +150 -0
  9. package/.github/copilot-instructions.md +143 -0
  10. package/.windsurf/rules/joopjs.md +226 -0
  11. package/CHANGELOG.md +81 -0
  12. package/README.md +47 -7
  13. package/ai-rules/AGENTS.md +241 -0
  14. package/ai-rules/GEMINI.md +183 -0
  15. package/dist/ai/index.js +15 -3
  16. package/dist/ai/index.js.map +1 -1
  17. package/dist/ai/index.mjs +15 -3
  18. package/dist/ai/index.mjs.map +1 -1
  19. package/dist/analytics/index.js +10 -2
  20. package/dist/analytics/index.js.map +1 -1
  21. package/dist/analytics/index.mjs +10 -2
  22. package/dist/analytics/index.mjs.map +1 -1
  23. package/dist/angular/index.d.mts +98 -27
  24. package/dist/angular/index.d.ts +98 -27
  25. package/dist/angular/index.js +44 -0
  26. package/dist/angular/index.js.map +1 -1
  27. package/dist/angular/index.mjs +39 -1
  28. package/dist/angular/index.mjs.map +1 -1
  29. package/dist/api/index.js +15 -3
  30. package/dist/api/index.js.map +1 -1
  31. package/dist/api/index.mjs +15 -3
  32. package/dist/api/index.mjs.map +1 -1
  33. package/dist/auth/index.js +15 -3
  34. package/dist/auth/index.js.map +1 -1
  35. package/dist/auth/index.mjs +15 -3
  36. package/dist/auth/index.mjs.map +1 -1
  37. package/dist/banking/index.js +15 -3
  38. package/dist/banking/index.js.map +1 -1
  39. package/dist/banking/index.mjs +15 -3
  40. package/dist/banking/index.mjs.map +1 -1
  41. package/dist/cache/index.js +15 -3
  42. package/dist/cache/index.js.map +1 -1
  43. package/dist/cache/index.mjs +15 -3
  44. package/dist/cache/index.mjs.map +1 -1
  45. package/dist/{index-DFqEoX_l.d.ts → consent.service-CIHNtx9h.d.ts} +1 -2
  46. package/dist/{index-B_ksKpS1.d.mts → consent.service-DQ-JAEJx.d.mts} +1 -2
  47. package/dist/core/index.d.mts +34 -1
  48. package/dist/core/index.d.ts +34 -1
  49. package/dist/core/index.js +56 -5
  50. package/dist/core/index.js.map +1 -1
  51. package/dist/core/index.mjs +54 -6
  52. package/dist/core/index.mjs.map +1 -1
  53. package/dist/deeplink/index.js +15 -3
  54. package/dist/deeplink/index.js.map +1 -1
  55. package/dist/deeplink/index.mjs +15 -3
  56. package/dist/deeplink/index.mjs.map +1 -1
  57. package/dist/device/index.js +15 -3
  58. package/dist/device/index.js.map +1 -1
  59. package/dist/device/index.mjs +15 -3
  60. package/dist/device/index.mjs.map +1 -1
  61. package/dist/forms/index.js +15 -3
  62. package/dist/forms/index.js.map +1 -1
  63. package/dist/forms/index.mjs +15 -3
  64. package/dist/forms/index.mjs.map +1 -1
  65. package/dist/i18n/index.js +15 -3
  66. package/dist/i18n/index.js.map +1 -1
  67. package/dist/i18n/index.mjs +15 -3
  68. package/dist/i18n/index.mjs.map +1 -1
  69. package/dist/index.d.mts +2 -2
  70. package/dist/index.d.ts +2 -2
  71. package/dist/index.js +50 -8
  72. package/dist/index.js.map +1 -1
  73. package/dist/index.mjs +50 -8
  74. package/dist/index.mjs.map +1 -1
  75. package/dist/{joop-CA3DMeOO.d.ts → joop-Dim2yEKG.d.ts} +1 -1
  76. package/dist/{joop-Bx7Iwj5p.d.mts → joop-GkQw13f9.d.mts} +1 -1
  77. package/dist/native-bridge/index.js +10 -2
  78. package/dist/native-bridge/index.js.map +1 -1
  79. package/dist/native-bridge/index.mjs +10 -2
  80. package/dist/native-bridge/index.mjs.map +1 -1
  81. package/dist/network/index.js +15 -3
  82. package/dist/network/index.js.map +1 -1
  83. package/dist/network/index.mjs +15 -3
  84. package/dist/network/index.mjs.map +1 -1
  85. package/dist/observability/index.js +15 -3
  86. package/dist/observability/index.js.map +1 -1
  87. package/dist/observability/index.mjs +15 -3
  88. package/dist/observability/index.mjs.map +1 -1
  89. package/dist/pwa/index.js +15 -3
  90. package/dist/pwa/index.js.map +1 -1
  91. package/dist/pwa/index.mjs +15 -3
  92. package/dist/pwa/index.mjs.map +1 -1
  93. package/dist/react/index.d.mts +2 -2
  94. package/dist/react/index.d.ts +2 -2
  95. package/dist/react/index.js +15 -3
  96. package/dist/react/index.js.map +1 -1
  97. package/dist/react/index.mjs +15 -3
  98. package/dist/react/index.mjs.map +1 -1
  99. package/dist/router/index.js +15 -3
  100. package/dist/router/index.js.map +1 -1
  101. package/dist/router/index.mjs +15 -3
  102. package/dist/router/index.mjs.map +1 -1
  103. package/dist/security/index.js +15 -3
  104. package/dist/security/index.js.map +1 -1
  105. package/dist/security/index.mjs +15 -3
  106. package/dist/security/index.mjs.map +1 -1
  107. package/dist/session/index.js +15 -3
  108. package/dist/session/index.js.map +1 -1
  109. package/dist/session/index.mjs +15 -3
  110. package/dist/session/index.mjs.map +1 -1
  111. package/dist/state/index.js +15 -3
  112. package/dist/state/index.js.map +1 -1
  113. package/dist/state/index.mjs +15 -3
  114. package/dist/state/index.mjs.map +1 -1
  115. package/dist/storage/index.js +15 -3
  116. package/dist/storage/index.js.map +1 -1
  117. package/dist/storage/index.mjs +15 -3
  118. package/dist/storage/index.mjs.map +1 -1
  119. package/dist/sync/index.js +15 -3
  120. package/dist/sync/index.js.map +1 -1
  121. package/dist/sync/index.mjs +15 -3
  122. package/dist/sync/index.mjs.map +1 -1
  123. package/dist/theme/index.js +15 -3
  124. package/dist/theme/index.js.map +1 -1
  125. package/dist/theme/index.mjs +15 -3
  126. package/dist/theme/index.mjs.map +1 -1
  127. package/dist/ui/index.js +15 -3
  128. package/dist/ui/index.js.map +1 -1
  129. package/dist/ui/index.mjs +15 -3
  130. package/dist/ui/index.mjs.map +1 -1
  131. package/dist/utilities/index.js +46 -4
  132. package/dist/utilities/index.js.map +1 -1
  133. package/dist/utilities/index.mjs +46 -4
  134. package/dist/utilities/index.mjs.map +1 -1
  135. package/dist/vue/index.d.mts +2 -2
  136. package/dist/vue/index.d.ts +2 -2
  137. package/dist/vue/index.js +15 -3
  138. package/dist/vue/index.js.map +1 -1
  139. package/dist/vue/index.mjs +15 -3
  140. package/dist/vue/index.mjs.map +1 -1
  141. package/dist/workflow/index.js +15 -3
  142. package/dist/workflow/index.js.map +1 -1
  143. package/dist/workflow/index.mjs +15 -3
  144. package/dist/workflow/index.mjs.map +1 -1
  145. package/package.json +96 -32
  146. package/scripts/setup-ai.mjs +133 -0
@@ -0,0 +1,235 @@
1
+ # /auth — JoopJS Authentication Services
2
+
3
+ > Author: Kundan Singh
4
+
5
+ All auth services are imported from `'joopjs'`. Instantiate as plain singletons.
6
+
7
+ ---
8
+
9
+ ## Auth Service
10
+
11
+ ```ts
12
+ import { JoopAuthService } from 'joopjs';
13
+ const auth = new JoopAuthService();
14
+
15
+ const session = await auth.login('alice@bank.com', 'password123');
16
+ // session: { userId, sessionId, accessToken, refreshToken, expiresAt }
17
+
18
+ const refreshed = await auth.refresh(session.refreshToken);
19
+ await auth.logout(session.sessionId);
20
+
21
+ const me = auth.getCurrentUser(); // JoopAuthUser | null
22
+ auth.session$().subscribe(session => { }); // reactive session changes
23
+
24
+ auth.onEvent('login', e => console.log('Login:', e.userId));
25
+ auth.onEvent('logout', e => console.log('Logout'));
26
+ ```
27
+
28
+ **Key types:** `JoopAuthUser`, `JoopAuthSession`, `JoopAuthToken`, `JoopAuthEvent`
29
+
30
+ ---
31
+
32
+ ## OTP Service
33
+
34
+ ```ts
35
+ import { JoopOtpService } from 'joopjs';
36
+ const otp = new JoopOtpService({ expiryMs: 5 * 60_000, length: 6 });
37
+
38
+ const token = otp.generate('u-001', 'login'); // { otp: '483920', expiresAt }
39
+ const ok = otp.verify('u-001', 'login', '483920'); // boolean
40
+ // auto-invalidated after single use
41
+
42
+ // TOTP (time-based)
43
+ const secret = otp.generateTotpSecret(); // base32 secret
44
+ const qrUri = otp.getTotpUri('alice@bank.com', secret, 'MyBank');
45
+ const valid = otp.verifyTotp(secret, userEnteredCode);
46
+ ```
47
+
48
+ ---
49
+
50
+ ## JWT Service
51
+
52
+ ```ts
53
+ import { JoopJwtService } from 'joopjs';
54
+ const jwt = new JoopJwtService({ secret: 'my-secret', expiryMs: 3600_000 });
55
+
56
+ const token = jwt.sign({ userId: 'u-001', role: 'admin' });
57
+ const claims = jwt.verify(token); // { userId, role, iat, exp } | null
58
+ const decoded = jwt.decode(token); // decode without verification
59
+ const renewed = jwt.renew(token); // extends expiry
60
+ const revoked = jwt.revoke(token); // blacklists token
61
+ const isRevoked = jwt.isRevoked(token); // boolean
62
+ ```
63
+
64
+ ---
65
+
66
+ ## MFA Service
67
+
68
+ ```ts
69
+ import { JoopMfaService } from 'joopjs';
70
+ const mfa = new JoopMfaService();
71
+
72
+ mfa.enroll('u-001', 'totp'); // or 'sms', 'email', 'hardware-key'
73
+ const challenge = mfa.challenge('u-001'); // { challengeId, method, hint }
74
+ const result = mfa.respond(challenge.challengeId, userCode);
75
+ // result: { passed: true, sessionUpgraded: true }
76
+
77
+ const methods = mfa.getEnrolledMethods('u-001'); // ['totp']
78
+ mfa.unenroll('u-001', 'totp');
79
+ mfa.status$().subscribe(event => { });
80
+ ```
81
+
82
+ ---
83
+
84
+ ## PKCE / OAuth 2.0
85
+
86
+ ```ts
87
+ import { JoopPkceService } from 'joopjs';
88
+ const pkce = new JoopPkceService({
89
+ clientId: 'my-app',
90
+ redirectUri: 'https://app.example.com/callback',
91
+ authorizationEndpoint: 'https://auth.bank.com/authorize',
92
+ tokenEndpoint: 'https://auth.bank.com/token',
93
+ scopes: ['openid', 'profile', 'accounts'],
94
+ });
95
+
96
+ const { url, codeVerifier, state } = pkce.buildAuthorizationUrl();
97
+ // Redirect user to `url`, store codeVerifier + state
98
+
99
+ const tokens = await pkce.exchangeCode(authorizationCode, codeVerifier);
100
+ // { accessToken, refreshToken, idToken, expiresIn }
101
+
102
+ const refreshed = await pkce.refreshTokens(tokens.refreshToken);
103
+ const claims = pkce.parseIdToken(tokens.idToken); // OIDC claims
104
+ ```
105
+
106
+ ---
107
+
108
+ ## OIDC Service
109
+
110
+ ```ts
111
+ import { JoopOidcService } from 'joopjs';
112
+ const oidc = new JoopOidcService({ issuer: 'https://auth.bank.com', clientId: 'my-app' });
113
+
114
+ await oidc.loadDiscovery(); // fetches /.well-known/openid-configuration
115
+ const url = oidc.buildLoginUrl({ redirectUri: 'https://app.example.com/callback', scopes: ['openid', 'email'] });
116
+ const user = await oidc.processCallback(callbackUrl); // JoopOidcUser
117
+ const info = await oidc.getUserInfo(accessToken);
118
+ await oidc.endSession(idToken, 'https://app.example.com');
119
+ ```
120
+
121
+ ---
122
+
123
+ ## SSO Service
124
+
125
+ ```ts
126
+ import { JoopSsoService } from 'joopjs';
127
+ const sso = new JoopSsoService();
128
+
129
+ sso.registerProvider({ id: 'azure-ad', name: 'Azure AD', type: 'oidc',
130
+ clientId: '...', clientSecret: '...', discoveryUrl: 'https://login.microsoftonline.com/.../v2.0' });
131
+
132
+ const loginUrl = sso.getLoginUrl('azure-ad', { redirectUri: '/callback' });
133
+ const session = await sso.handleCallback('azure-ad', callbackParams);
134
+ // session.user: { id, email, name, groups, provider }
135
+
136
+ sso.setRoleMapping('azure-ad', { 'BankAdmins': 'admin', 'BankUsers': 'user' });
137
+ const appRole = sso.mapRole('azure-ad', 'BankAdmins'); // 'admin'
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Biometric Auth
143
+
144
+ ```ts
145
+ import { JoopBiometricAuthService } from 'joopjs';
146
+ const bio = new JoopBiometricAuthService();
147
+
148
+ const available = await bio.isAvailable(); // { fingerprint, faceId, iris }
149
+ await bio.enroll('u-001', 'fingerprint');
150
+ const result = await bio.authenticate('u-001', 'fingerprint');
151
+ // { authenticated: true, confidence: 0.98, method: 'fingerprint' }
152
+
153
+ bio.revoke('u-001', 'fingerprint');
154
+ bio.authEvent$().subscribe(event => { });
155
+ ```
156
+
157
+ ---
158
+
159
+ ## Permission / RBAC
160
+
161
+ ```ts
162
+ import { JoopPermissionService } from 'joopjs';
163
+ const perms = new JoopPermissionService();
164
+
165
+ perms.defineRole('admin', ['accounts:read', 'accounts:write', 'transfers:approve']);
166
+ perms.defineRole('teller', ['accounts:read', 'transfers:initiate']);
167
+
168
+ perms.assignRole('u-001', 'admin');
169
+ perms.grantPermission('u-002', 'reports:read');
170
+
171
+ const can = perms.check('u-001', 'transfers:approve'); // true
172
+ const all = perms.getPermissions('u-001'); // string[]
173
+ perms.revokeRole('u-001', 'admin');
174
+ ```
175
+
176
+ ---
177
+
178
+ ## Rate Limiter (Auth Protection)
179
+
180
+ ```ts
181
+ import { JoopRateLimiterService } from 'joopjs';
182
+ const rl = new JoopRateLimiterService();
183
+
184
+ rl.configure('login', { maxAttempts: 5, windowMs: 15 * 60_000, blockDurationMs: 30 * 60_000 });
185
+
186
+ const result = rl.check('login', 'u-001');
187
+ // { allowed: true, remaining: 4, resetAt: number }
188
+
189
+ rl.record('login', 'u-001', false); // record failed attempt
190
+ rl.record('login', 'u-001', true); // success — resets counter
191
+ rl.block('login', 'u-001'); // force block
192
+ const status = rl.getStatus('login', 'u-001');
193
+ // { blocked: false, attempts: 2, remaining: 3, resetAt }
194
+ ```
195
+
196
+ ---
197
+
198
+ ## Session Management
199
+
200
+ ```ts
201
+ import { JoopSessionService } from 'joopjs';
202
+ const sessions = new JoopSessionService({ idleTimeoutMs: 15 * 60_000, absoluteTimeoutMs: 8 * 3600_000 });
203
+
204
+ const s = sessions.create({ userId: 'u-001', ipAddress: '10.0.0.1', userAgent: 'Chrome/120' });
205
+ sessions.touch(s.id); // reset idle timer
206
+ const valid = sessions.validate(s.id); // { valid, reason? }
207
+ sessions.invalidate(s.id);
208
+ sessions.invalidateAll('u-001'); // logout all devices
209
+ sessions.expired$().subscribe(s => console.log('Session expired:', s.id));
210
+ ```
211
+
212
+ ---
213
+
214
+ ## What Gets Published to npm
215
+
216
+ Controlled by `"files"` in `package.json` — acts as an allowlist. Only these two are included:
217
+
218
+ | Published to npm | Never published |
219
+ |-----------------|----------------|
220
+ | `dist/` — ESM + CJS + `.d.ts` for all 34 sub-paths | `src/` — TypeScript source |
221
+ | `CHANGELOG.md` — release history | `tests/` — test suite |
222
+ | | `scripts/` — release automation |
223
+ | | `playground/` — Vite demo app |
224
+ | | `.claude/` — Claude skills (including this file) |
225
+ | | `.cursor/` — Cursor rules |
226
+ | | `.windsurf/` — Windsurf rules |
227
+ | | `GEMINI.md`, `AGENTS.md` — AI tool instructions |
228
+ | | `tsup.config.ts`, `vitest.config.ts`, `tsconfig.json` |
229
+
230
+ Source code, AI rules, and dev tooling are **never** published to npm.
231
+
232
+ Verify the tarball contents before any publish:
233
+ ```bash
234
+ npm pack --dry-run
235
+ ```
@@ -0,0 +1,377 @@
1
+ # /banking — JoopJS Banking Services
2
+
3
+ > Author: Kundan Singh
4
+
5
+ All banking services are imported from `'joopjs'`. Instantiate as plain singletons.
6
+
7
+ ---
8
+
9
+ ## Digital Wallet
10
+
11
+ ```ts
12
+ import { JoopDigitalWalletService } from 'joopjs';
13
+ const wallet = new JoopDigitalWalletService();
14
+
15
+ const w = wallet.createWallet('user-001', { currency: 'USD', label: 'Primary' });
16
+ wallet.topUp(w.id, 500);
17
+ wallet.pay(w.id, 50, 'merchant-1', 'Coffee Shop');
18
+ wallet.transfer(w.id, otherWalletId, 100);
19
+ wallet.freeze(w.id); wallet.unfreeze(w.id); wallet.close(w.id); // requires zero balance
20
+ const bal = wallet.getBalance(w.id); // number
21
+ const txns = wallet.getTransactions(w.id, 20); // last 20, newest first
22
+ wallet.balance$().subscribe(({ walletId, balance }) => { }); // reactive
23
+ ```
24
+
25
+ **Key types:** `JoopWallet`, `JoopWalletTransaction`, `JoopWalletStatus`, `JoopWalletTxnType`, `JoopTransferResult`
26
+
27
+ ---
28
+
29
+ ## Loan Servicing
30
+
31
+ ```ts
32
+ import { JoopLoanServicingService } from 'joopjs';
33
+ const loans = new JoopLoanServicingService();
34
+
35
+ const loan = loans.createLoan({
36
+ borrowerName: 'Alice', borrowerId: 'u-001',
37
+ principalAmount: 120_000, annualInterestRatePercent: 12, tenureMonths: 24,
38
+ currency: 'USD',
39
+ });
40
+ // loan.ref → 'LN-2026-000001', loan.emiAmount auto-calculated
41
+
42
+ loans.recordPayment(loan.id, loan.emiAmount); // interest-first allocation
43
+ const sched = loans.getSchedule(loan.id); // JoopInstallment[]
44
+ const stmt = loans.getLoanStatement(loan.id); // full statement
45
+ const bal = loans.getOutstandingBalance(loan.id); // number
46
+ loans.markDefaulted(loan.id);
47
+ loans.waveInstallment(loan.id, 3); // waive installment #3
48
+ ```
49
+
50
+ **Key types:** `JoopLoanAccount`, `JoopInstallment`, `JoopLoanPayment`, `JoopLoanStatement`, `JoopLoanAccountStatus`, `JoopRepaymentStatus`
51
+
52
+ ---
53
+
54
+ ## FX Forward Contracts
55
+
56
+ ```ts
57
+ import { JoopFxForwardService } from 'joopjs';
58
+ const fx = new JoopFxForwardService();
59
+
60
+ fx.setSpotRate('USD', 'EUR', 0.92); // auto-creates inverse
61
+ const fwd = fx.createForward({
62
+ type: 'sell', baseCurrency: 'USD', quoteCurrency: 'EUR',
63
+ notionalAmount: 100_000, contractRate: 0.92,
64
+ maturityDate: Date.now() + 90 * 86_400_000,
65
+ });
66
+ // fwd.ref → 'FWD-2026-000001'
67
+
68
+ const settlement = fx.settleForward(fwd.id, 0.90); // settlement.pnl = 2000
69
+ fx.cancelForward(fwd.id);
70
+ const mtm = fx.getMarkToMarket(fwd.id); // unrealized P&L
71
+ const exp = fx.getExposure('USD'); // JoopFxExposure[]
72
+ const expiring = fx.getExpiring(7 * 86_400_000); // due in 7 days
73
+ ```
74
+
75
+ **Key types:** `JoopFxForward`, `JoopForwardSettlement`, `JoopFxExposure`, `JoopForwardType`, `JoopForwardStatus`
76
+
77
+ ---
78
+
79
+ ## Ledger (Double-Entry Bookkeeping)
80
+
81
+ ```ts
82
+ import { JoopLedgerService } from 'joopjs';
83
+ const ledger = new JoopLedgerService();
84
+
85
+ ledger.addAccount('1001', 'Cash', 'asset');
86
+ ledger.addAccount('4001', 'Sales Revenue','revenue');
87
+
88
+ const entry = ledger.postEntry('Sale', [
89
+ { accountCode: '1001', debit: 1000, credit: 0 },
90
+ { accountCode: '4001', debit: 0, credit: 1000 },
91
+ ]);
92
+ // entry.ref → 'JE-2026-000001'
93
+
94
+ const bal = ledger.getBalance('1001'); // 1000
95
+ const trial = ledger.getTrialBalance(); // { rows, totalDebits, totalCredits, isBalanced }
96
+ ```
97
+
98
+ **Key types:** `JoopAccountType` ('asset'|'liability'|'equity'|'revenue'|'expense'), `JoopLedgerAccount`, `JoopJournalEntry`, `JoopTrialBalance`
99
+
100
+ ---
101
+
102
+ ## Reconciliation
103
+
104
+ ```ts
105
+ import { JoopReconciliationService } from 'joopjs';
106
+ const recon = new JoopReconciliationService();
107
+
108
+ const session = recon.createSession('ACC-001', bankItems, internalItems);
109
+ recon.autoMatch(session.id, 0, 3); // tolerance: $0, 3 days
110
+ recon.manualMatch(session.id, internalId, bankId);
111
+ const summary = recon.getSummary(session.id);
112
+ // { matchedPairs, unmatchedInternal, unmatchedBank, totalDifference, isReconciled }
113
+ ```
114
+
115
+ ---
116
+
117
+ ## Limit Management
118
+
119
+ ```ts
120
+ import { JoopLimitManagementService } from 'joopjs';
121
+ const limits = new JoopLimitManagementService();
122
+
123
+ limits.setLimit({
124
+ scope: 'account', scopeId: 'ACC-001', type: 'daily',
125
+ currency: 'USD', maxAmount: 10_000, enabled: true,
126
+ });
127
+ const check = limits.checkLimit('account', 'ACC-001', 'daily', 'USD', 5000);
128
+ // null = no limit configured; { allowed, remaining, used } if configured
129
+ limits.recordUsage('account', 'ACC-001', 'daily', 'USD', 5000);
130
+ ```
131
+
132
+ **Key types:** `JoopLimitType` ('per-transaction'|'daily'|'weekly'|'monthly'|'yearly'), `JoopLimitScope`, `JoopLimit`, `JoopLimitCheckResult`
133
+
134
+ ---
135
+
136
+ ## Standing Orders
137
+
138
+ ```ts
139
+ import { JoopStandingOrderService } from 'joopjs';
140
+ const so = new JoopStandingOrderService();
141
+
142
+ const order = so.create({
143
+ fromAccount: 'ACC-001', toAccount: 'ACC-002',
144
+ toBeneficiaryName: 'Rent', amount: 1500,
145
+ frequency: 'monthly', startDate: Date.now(),
146
+ currency: 'USD',
147
+ });
148
+ // order.ref → 'SO-2026-000001'
149
+
150
+ const exec = so.execute(order.id, 'success'); // advances nextExecutionAt
151
+ const due = so.getDue(); // orders due now
152
+ so.pause(order.id); so.resume(order.id); so.cancel(order.id);
153
+ ```
154
+
155
+ ---
156
+
157
+ ## Insurance
158
+
159
+ ```ts
160
+ import { JoopInsuranceService } from 'joopjs';
161
+ const ins = new JoopInsuranceService();
162
+
163
+ const policy = ins.addPolicy({
164
+ type: 'life', holderName: 'Alice', holderId: 'u-001',
165
+ coverageAmount: 500_000, annualPremium: 2400,
166
+ startDate: Date.now(), endDate: Date.now() + 365 * 86_400_000,
167
+ frequency: 'monthly',
168
+ });
169
+ ins.fileClaim(policy.id, 1000, 'Hospital stay'); // claimNumber CLM-2026-000001
170
+ ins.renewPolicy(policy.id, Date.now() + 730 * 86_400_000);
171
+ ins.recordPremiumPayment(policy.id, 200);
172
+ const expiring = ins.getExpiring(30 * 86_400_000); // expiring in 30 days
173
+ ```
174
+
175
+ ---
176
+
177
+ ## Remittance
178
+
179
+ ```ts
180
+ import { JoopRemittanceService } from 'joopjs';
181
+ const rem = new JoopRemittanceService();
182
+
183
+ rem.setExchangeRate('USD', 'INR', 83.5);
184
+ const quote = rem.getQuote('USD', 'INR', 1000);
185
+ // { sendAmount:1000, fee:25, receiveAmount:81543.75, exchangeRate:83.5 }
186
+
187
+ const tx = rem.initiate({
188
+ senderId: 'u-001', senderName: 'Alice',
189
+ receiverId: 'u-002', receiverName: 'Bob',
190
+ sourceCurrency: 'USD', targetCurrency: 'INR',
191
+ sendAmount: 1000, channel: 'swift',
192
+ });
193
+ // tx.ref → 'RMT-2026-000001'
194
+
195
+ rem.updateStatus(tx.id, 'completed');
196
+ const history = rem.getHistory('u-001', 5); // last 5, newest first
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Virtual Accounts
202
+
203
+ ```ts
204
+ import { JoopVirtualAccountService } from 'joopjs';
205
+ const va = new JoopVirtualAccountService();
206
+
207
+ const acct = va.create({ parentAccountId: 'ACC-001', purpose: 'escrow', currency: 'USD', expectedAmount: 5000 });
208
+ va.recordCredit(acct.id, 2500, { reference: 'INV-001' });
209
+ const full = va.isFullyCollected(acct.id); // false (2500 of 5000)
210
+ va.close(acct.id);
211
+ const byRef = va.getByReference(acct.accountNumber);
212
+ ```
213
+
214
+ ---
215
+
216
+ ## Card Management
217
+
218
+ ```ts
219
+ import { JoopCardManagementService } from 'joopjs';
220
+ const cards = new JoopCardManagementService();
221
+
222
+ const card = cards.issueCard({ userId: 'u-001', cardType: 'virtual', network: 'visa', currency: 'USD' });
223
+ cards.freeze(card.id); cards.unfreeze(card.id);
224
+ cards.setSpendingLimits(card.id, { daily: 500, monthly: 5000, perTransaction: 200 });
225
+ cards.setControls(card.id, { onlineAllowed: true, internationalAllowed: false });
226
+ const check = cards.checkTransaction(card.id, { amount: 100, merchant: 'Amazon', channel: 'online' });
227
+ // { allowed: true, reason: null }
228
+ ```
229
+
230
+ ---
231
+
232
+ ## Dispute Management
233
+
234
+ ```ts
235
+ import { JoopDisputeService } from 'joopjs';
236
+ const disputes = new JoopDisputeService();
237
+
238
+ const d = disputes.file({ userId: 'u-001', transactionId: 'TXN-001', reason: 'unauthorized', amount: 99.99 });
239
+ disputes.uploadEvidence(d.id, { type: 'screenshot', description: 'Charge I did not make' });
240
+ disputes.resolve(d.id, 'resolved-in-favor', 'Refund issued');
241
+ const stats = disputes.getStats();
242
+ ```
243
+
244
+ ---
245
+
246
+ ## Bill Payment
247
+
248
+ ```ts
249
+ import { JoopBillPaymentService } from 'joopjs';
250
+ const bills = new JoopBillPaymentService();
251
+
252
+ bills.addBiller({ id: 'ELEC-001', name: 'City Electric', category: 'utilities', accountFormat: 'ACCT-####' });
253
+ const bill = bills.createBill({ billerId: 'ELEC-001', accountNumber: 'ACCT-1234', amount: 150, dueDate: Date.now() + 7 * 86_400_000 });
254
+ const payment = bills.pay(bill.id, 150, 'wallet');
255
+ const upcoming = bills.getUpcoming(7 * 86_400_000); // due in 7 days
256
+ ```
257
+
258
+ ---
259
+
260
+ ## Statement Generator
261
+
262
+ ```ts
263
+ import { JoopStatementGeneratorService } from 'joopjs';
264
+ const gen = new JoopStatementGeneratorService();
265
+
266
+ gen.addTransaction({ id: 'T1', date: Date.now(), amount: -50, description: 'Coffee', type: 'debit', balance: 950 });
267
+ const stmt = gen.generate({ accountId: 'ACC-001', from: startTs, to: endTs, format: 'pdf' });
268
+ ```
269
+
270
+ ---
271
+
272
+ ## Beneficiary
273
+
274
+ ```ts
275
+ import { JoopBeneficiaryService } from 'joopjs';
276
+ const bene = new JoopBeneficiaryService();
277
+
278
+ const b = bene.add({ userId: 'u-001', name: 'Bob', type: 'bank', accountNumber: '12345678', bankCode: 'HSBC' });
279
+ const v = bene.validate(b.id); // { valid, errors }
280
+ const list = bene.getAll('u-001');
281
+ bene.remove(b.id);
282
+ ```
283
+
284
+ ---
285
+
286
+ ## Mandate (Direct Debit)
287
+
288
+ ```ts
289
+ import { JoopMandateService } from 'joopjs';
290
+ const mandates = new JoopMandateService();
291
+
292
+ const m = mandates.create({ userId: 'u-001', creditorName: 'Netflix', amount: 15.99, currency: 'USD', frequency: 'monthly', startDate: Date.now() });
293
+ mandates.execute(m.id, 15.99); // records execution
294
+ mandates.pause(m.id); mandates.resume(m.id); mandates.cancel(m.id);
295
+ const due = mandates.getDue();
296
+ ```
297
+
298
+ ---
299
+
300
+ ## Split Payment
301
+
302
+ ```ts
303
+ import { JoopSplitPaymentService } from 'joopjs';
304
+ const split = new JoopSplitPaymentService();
305
+
306
+ const exp = split.createExpense({ title: 'Dinner', totalAmount: 120, paidById: 'u-001', method: 'equal' });
307
+ split.addParticipant(exp.id, { userId: 'u-002', name: 'Bob' });
308
+ split.addParticipant(exp.id, { userId: 'u-003', name: 'Carol' });
309
+ split.settle(exp.id, 'u-002'); // mark u-002 as settled
310
+ const balances = split.getBalances(exp.id);
311
+ ```
312
+
313
+ ---
314
+
315
+ ## Payment URI (QR / UPI / SEPA)
316
+
317
+ ```ts
318
+ import { JoopPaymentUriService } from 'joopjs';
319
+ const uri = new JoopPaymentUriService();
320
+
321
+ const upiUri = uri.generate({ scheme: 'upi', payeeVpa: 'merchant@bank', amount: 100, currency: 'INR' });
322
+ const parsed = uri.parse('upi://pay?pa=merchant@bank&am=100');
323
+ ```
324
+
325
+ ---
326
+
327
+ ## Open Banking
328
+
329
+ ```ts
330
+ import { JoopOpenBankingClient } from 'joopjs';
331
+ const ob = new JoopOpenBankingClient({ baseUrl: 'https://api.bank.com', clientId: 'my-app' });
332
+
333
+ const consent = await ob.requestConsent({ permissions: ['accounts', 'transactions'], userId: 'u-001' });
334
+ const accounts = await ob.getAccounts(consent.consentId);
335
+ const txns = await ob.getTransactions(accounts[0].accountId, { from: startTs, to: endTs });
336
+ const payment = await ob.initiatePayment({ debtorAccount: 'ACC-001', creditorAccount: 'ACC-002', amount: 500 });
337
+ ```
338
+
339
+ ---
340
+
341
+ ## Chequebook
342
+
343
+ ```ts
344
+ import { JoopChequebookService } from 'joopjs';
345
+ const cheques = new JoopChequebookService();
346
+
347
+ const book = cheques.requestChequebook({ accountId: 'ACC-001', leaves: 25 });
348
+ cheques.activateChequebook(book.id);
349
+ const leaf = cheques.issueLeaf(book.id, { payeeName: 'Alice', amount: 500, date: Date.now() });
350
+ cheques.markCleared(leaf.chequeNumber);
351
+ const stats = cheques.getStats(book.id);
352
+ ```
353
+
354
+ ---
355
+
356
+ ## What Gets Published to npm
357
+
358
+ Controlled by `"files"` in `package.json` — acts as an allowlist. Only these two are included:
359
+
360
+ | Published to npm | Never published |
361
+ |-----------------|----------------|
362
+ | `dist/` — ESM + CJS + `.d.ts` for all 34 sub-paths | `src/` — TypeScript source |
363
+ | `CHANGELOG.md` — release history | `tests/` — test suite |
364
+ | | `scripts/` — release automation |
365
+ | | `playground/` — Vite demo app |
366
+ | | `.claude/` — Claude skills (including this file) |
367
+ | | `.cursor/` — Cursor rules |
368
+ | | `.windsurf/` — Windsurf rules |
369
+ | | `GEMINI.md`, `AGENTS.md` — AI tool instructions |
370
+ | | `tsup.config.ts`, `vitest.config.ts`, `tsconfig.json` |
371
+
372
+ Source code, AI rules, and dev tooling are **never** published to npm.
373
+
374
+ Verify the tarball contents before any publish:
375
+ ```bash
376
+ npm pack --dry-run
377
+ ```