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.
- package/.claude/skills/auth.md +235 -0
- package/.claude/skills/banking.md +377 -0
- package/.claude/skills/encryption.md +265 -0
- package/.claude/skills/finance.md +248 -0
- package/.claude/skills/observables.md +270 -0
- package/.claude/skills/security.md +240 -0
- package/.claude/skills/setup.md +196 -0
- package/.cursor/rules/joopjs.mdc +150 -0
- package/.github/copilot-instructions.md +143 -0
- package/.windsurf/rules/joopjs.md +226 -0
- package/CHANGELOG.md +81 -0
- package/README.md +47 -7
- package/ai-rules/AGENTS.md +241 -0
- package/ai-rules/GEMINI.md +183 -0
- package/dist/ai/index.js +15 -3
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/index.mjs +15 -3
- package/dist/ai/index.mjs.map +1 -1
- package/dist/analytics/index.js +10 -2
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +10 -2
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/angular/index.d.mts +98 -27
- package/dist/angular/index.d.ts +98 -27
- package/dist/angular/index.js +44 -0
- package/dist/angular/index.js.map +1 -1
- package/dist/angular/index.mjs +39 -1
- package/dist/angular/index.mjs.map +1 -1
- package/dist/api/index.js +15 -3
- package/dist/api/index.js.map +1 -1
- package/dist/api/index.mjs +15 -3
- package/dist/api/index.mjs.map +1 -1
- package/dist/auth/index.js +15 -3
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/index.mjs +15 -3
- package/dist/auth/index.mjs.map +1 -1
- package/dist/banking/index.js +15 -3
- package/dist/banking/index.js.map +1 -1
- package/dist/banking/index.mjs +15 -3
- package/dist/banking/index.mjs.map +1 -1
- package/dist/cache/index.js +15 -3
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/index.mjs +15 -3
- package/dist/cache/index.mjs.map +1 -1
- package/dist/{index-DFqEoX_l.d.ts → consent.service-CIHNtx9h.d.ts} +1 -2
- package/dist/{index-B_ksKpS1.d.mts → consent.service-DQ-JAEJx.d.mts} +1 -2
- package/dist/core/index.d.mts +34 -1
- package/dist/core/index.d.ts +34 -1
- package/dist/core/index.js +56 -5
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +54 -6
- package/dist/core/index.mjs.map +1 -1
- package/dist/deeplink/index.js +15 -3
- package/dist/deeplink/index.js.map +1 -1
- package/dist/deeplink/index.mjs +15 -3
- package/dist/deeplink/index.mjs.map +1 -1
- package/dist/device/index.js +15 -3
- package/dist/device/index.js.map +1 -1
- package/dist/device/index.mjs +15 -3
- package/dist/device/index.mjs.map +1 -1
- package/dist/forms/index.js +15 -3
- package/dist/forms/index.js.map +1 -1
- package/dist/forms/index.mjs +15 -3
- package/dist/forms/index.mjs.map +1 -1
- package/dist/i18n/index.js +15 -3
- package/dist/i18n/index.js.map +1 -1
- package/dist/i18n/index.mjs +15 -3
- package/dist/i18n/index.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +50 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +50 -8
- package/dist/index.mjs.map +1 -1
- package/dist/{joop-CA3DMeOO.d.ts → joop-Dim2yEKG.d.ts} +1 -1
- package/dist/{joop-Bx7Iwj5p.d.mts → joop-GkQw13f9.d.mts} +1 -1
- package/dist/native-bridge/index.js +10 -2
- package/dist/native-bridge/index.js.map +1 -1
- package/dist/native-bridge/index.mjs +10 -2
- package/dist/native-bridge/index.mjs.map +1 -1
- package/dist/network/index.js +15 -3
- package/dist/network/index.js.map +1 -1
- package/dist/network/index.mjs +15 -3
- package/dist/network/index.mjs.map +1 -1
- package/dist/observability/index.js +15 -3
- package/dist/observability/index.js.map +1 -1
- package/dist/observability/index.mjs +15 -3
- package/dist/observability/index.mjs.map +1 -1
- package/dist/pwa/index.js +15 -3
- package/dist/pwa/index.js.map +1 -1
- package/dist/pwa/index.mjs +15 -3
- package/dist/pwa/index.mjs.map +1 -1
- package/dist/react/index.d.mts +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +15 -3
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +15 -3
- package/dist/react/index.mjs.map +1 -1
- package/dist/router/index.js +15 -3
- package/dist/router/index.js.map +1 -1
- package/dist/router/index.mjs +15 -3
- package/dist/router/index.mjs.map +1 -1
- package/dist/security/index.js +15 -3
- package/dist/security/index.js.map +1 -1
- package/dist/security/index.mjs +15 -3
- package/dist/security/index.mjs.map +1 -1
- package/dist/session/index.js +15 -3
- package/dist/session/index.js.map +1 -1
- package/dist/session/index.mjs +15 -3
- package/dist/session/index.mjs.map +1 -1
- package/dist/state/index.js +15 -3
- package/dist/state/index.js.map +1 -1
- package/dist/state/index.mjs +15 -3
- package/dist/state/index.mjs.map +1 -1
- package/dist/storage/index.js +15 -3
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/index.mjs +15 -3
- package/dist/storage/index.mjs.map +1 -1
- package/dist/sync/index.js +15 -3
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/index.mjs +15 -3
- package/dist/sync/index.mjs.map +1 -1
- package/dist/theme/index.js +15 -3
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +15 -3
- package/dist/theme/index.mjs.map +1 -1
- package/dist/ui/index.js +15 -3
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/index.mjs +15 -3
- package/dist/ui/index.mjs.map +1 -1
- package/dist/utilities/index.js +46 -4
- package/dist/utilities/index.js.map +1 -1
- package/dist/utilities/index.mjs +46 -4
- package/dist/utilities/index.mjs.map +1 -1
- package/dist/vue/index.d.mts +2 -2
- package/dist/vue/index.d.ts +2 -2
- package/dist/vue/index.js +15 -3
- package/dist/vue/index.js.map +1 -1
- package/dist/vue/index.mjs +15 -3
- package/dist/vue/index.mjs.map +1 -1
- package/dist/workflow/index.js +15 -3
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/index.mjs +15 -3
- package/dist/workflow/index.mjs.map +1 -1
- package/package.json +96 -32
- package/scripts/setup-ai.mjs +133 -0
package/README.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
**Enterprise-grade, framework-agnostic TypeScript SDK for financial applications.**
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/joopjs)
|
|
6
6
|
[](./LICENSE)
|
|
7
7
|
[](https://nodejs.org)
|
|
8
|
-
[](#)
|
|
9
9
|
[](https://kundan-leo.github.io/JoopJs/demo/)
|
|
10
10
|
[](https://github.com/kundan-leo/JoopJs)
|
|
11
11
|
|
|
@@ -17,11 +17,14 @@
|
|
|
17
17
|
|
|
18
18
|
JoopJS provides 80+ production-ready services covering banking, finance, security, authentication, and encryption — all as plain TypeScript classes with no framework dependencies. Works with **React 18+**, **Angular 17+**, **Vue 3+**, or any plain TypeScript/JavaScript project.
|
|
19
19
|
|
|
20
|
+
**New in 2.1.0:** real Angular 17+ DI in `joopjs/angular` (`provideJoopAngular`, `injectJoop`, `joopSignal`), federation-safe singletons in `joopjs/core` (`globalSingleton`, `joopCopyCount`, `silenceDuplicateCopyWarning`) for Module Federation micro-frontends, and error-isolated observable emission.
|
|
21
|
+
|
|
20
22
|
---
|
|
21
23
|
|
|
22
24
|
## Table of Contents
|
|
23
25
|
|
|
24
26
|
- [Install](#install)
|
|
27
|
+
- [AI Assistant Setup](#ai-assistant-setup)
|
|
25
28
|
- [Quick Start](#quick-start)
|
|
26
29
|
- [Service Catalogue](#service-catalogue)
|
|
27
30
|
- [Banking](#banking-26-services)
|
|
@@ -60,6 +63,33 @@ No mandatory peer dependencies — React / Angular / Vue bindings are opt-in.
|
|
|
60
63
|
|
|
61
64
|
---
|
|
62
65
|
|
|
66
|
+
## AI Assistant Setup
|
|
67
|
+
|
|
68
|
+
JoopJS ships rules for all major AI coding tools. One command sets them up in your project:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npx joopjs setup-ai
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This copies joopjs-aware rules so that **Claude Code**, **Cursor**, **Windsurf**, **GitHub Copilot**, **Gemini CLI**, and **Codex** all understand the joopjs API — correct imports, service patterns, observable rules, and data types.
|
|
75
|
+
|
|
76
|
+
| Tool | What gets copied |
|
|
77
|
+
|------|-----------------|
|
|
78
|
+
| Claude Code | `.claude/skills/joopjs-*.md` — 7 skills (setup, banking, finance, security, auth, encryption, observables) |
|
|
79
|
+
| Cursor | `.cursor/rules/joopjs.mdc` — auto-applied to all `.ts` / `.tsx` / `.js` files |
|
|
80
|
+
| Windsurf | `.windsurf/rules/joopjs.md` — loaded by Cascade automatically |
|
|
81
|
+
| GitHub Copilot | `.github/copilot-instructions.md` |
|
|
82
|
+
| Gemini CLI | `GEMINI.md` |
|
|
83
|
+
| Codex / OpenAI | `AGENTS.md` |
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npx joopjs setup-ai --force # overwrite after upgrading joopjs
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
> **Claude Code users:** after running, the command output shows the exact skill lines to add to your `CLAUDE.md`.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
63
93
|
## Quick Start
|
|
64
94
|
|
|
65
95
|
### 1. Initialise
|
|
@@ -446,27 +476,33 @@ function BalanceCard({ walletId }: { walletId: string }) {
|
|
|
446
476
|
|
|
447
477
|
### Angular
|
|
448
478
|
|
|
479
|
+
Real Angular 17+ DI. Only the `joopjs/angular` adapter imports `@angular/*` — the core SDK stays framework-agnostic.
|
|
480
|
+
|
|
449
481
|
```typescript
|
|
450
482
|
// app.config.ts (standalone)
|
|
451
483
|
import { ApplicationConfig } from '@angular/core';
|
|
452
|
-
import {
|
|
484
|
+
import { provideJoopAngular } from 'joopjs/angular';
|
|
453
485
|
import { createJoop } from 'joopjs';
|
|
454
486
|
|
|
455
487
|
export const appConfig: ApplicationConfig = {
|
|
456
488
|
providers: [
|
|
457
|
-
|
|
489
|
+
...provideJoopAngular(createJoop({ env: 'production', appId: 'my-app' })),
|
|
458
490
|
],
|
|
459
491
|
};
|
|
460
492
|
```
|
|
461
493
|
|
|
462
494
|
```typescript
|
|
463
495
|
// component.ts
|
|
464
|
-
import {
|
|
465
|
-
import {
|
|
496
|
+
import { injectJoop, joopSignal } from 'joopjs/angular';
|
|
497
|
+
import { JoopAuthService } from 'joopjs';
|
|
466
498
|
|
|
467
499
|
@Component({ ... })
|
|
468
500
|
export class LoginComponent {
|
|
469
|
-
private
|
|
501
|
+
private joop = injectJoop(); // typed JoopInstance via JOOP_INSTANCE
|
|
502
|
+
private auth = new JoopAuthService();
|
|
503
|
+
|
|
504
|
+
// Bridge any JoopJS observable to an Angular signal — auto-teardown via DestroyRef
|
|
505
|
+
session = joopSignal(this.auth.session$());
|
|
470
506
|
|
|
471
507
|
async login(email: string, password: string) {
|
|
472
508
|
const session = await this.auth.login(email, password);
|
|
@@ -475,6 +511,8 @@ export class LoginComponent {
|
|
|
475
511
|
}
|
|
476
512
|
```
|
|
477
513
|
|
|
514
|
+
> Granular injection tokens still exist, and `joopAuthGuard()` (a `CanActivateFn`) plus `joopAuthInterceptor()` (an `HttpInterceptorFn`) are available for routing and HTTP. `provideJoop()`, `JoopModule`, and `fromJoop()` are deprecated but still functional.
|
|
515
|
+
|
|
478
516
|
### Vue
|
|
479
517
|
|
|
480
518
|
```typescript
|
|
@@ -533,6 +571,8 @@ unsub();
|
|
|
533
571
|
|
|
534
572
|
**Critical rule:** Always use `.next(value)` to emit — **never `.emit(value)`** (that method does not exist).
|
|
535
573
|
|
|
574
|
+
Emission is **error-isolated**: `next()` wraps each subscriber call in try/catch over a snapshot of its listeners, so one bad subscriber won't break the stream for the others. Route subscriber errors anywhere with `setSubjectErrorHandler` (default: `console.error`).
|
|
575
|
+
|
|
536
576
|
---
|
|
537
577
|
|
|
538
578
|
## TypeScript Types
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# AGENTS.md — JoopJS SDK
|
|
2
|
+
|
|
3
|
+
This file instructs AI coding agents (Codex, OpenAI Agents, and similar) on how to generate code using the **joopjs** SDK.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Package
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install joopjs
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**joopjs** is a framework-agnostic TypeScript SDK for enterprise financial applications. Works with React, Angular, Vue, Node.js, and plain TypeScript.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Import Rules
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
// Main entry — everything
|
|
21
|
+
import { JoopAuthService, JoopDigitalWalletService } from 'joopjs';
|
|
22
|
+
|
|
23
|
+
// Sub-path imports (tree-shakeable, preferred)
|
|
24
|
+
import { JoopGcmService } from 'joopjs/encryption';
|
|
25
|
+
import { JoopAuthService } from 'joopjs/auth';
|
|
26
|
+
import { JoopCacheService } from 'joopjs/cache';
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Available sub-paths: `auth`, `encryption`, `banking`, `security`, `api`, `core`, `session`, `device`, `observability`, `theme`, `i18n`, `ui`, `forms`, `router`, `state`, `workers`, `workflow`, `sync`, `platform`, `cache`, `network`, `analytics`, `validation`, `utilities`, `pwa`, `native-bridge`, `deeplink`, `react`, `angular`, `vue`, `india`.
|
|
30
|
+
|
|
31
|
+
**Never** import from internal paths (`joopjs/src/...`, `joopjs/dist/...`).
|
|
32
|
+
|
|
33
|
+
`joopjs/core` exposes `globalSingleton`, `joopCopyCount`, and `silenceDuplicateCopyWarning`. In Module Federation micro-frontends, share joopjs as a singleton (`shared: { joopjs: { singleton: true } }`) so logger/config/event-bus state does not fragment across multiple loaded copies.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Service Instantiation
|
|
38
|
+
|
|
39
|
+
All services are plain classes — no DI framework, no `@Injectable`, no container:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
const wallet = new JoopDigitalWalletService();
|
|
43
|
+
const auth = new JoopAuthService();
|
|
44
|
+
const gcm = new JoopGcmService();
|
|
45
|
+
const loans = new JoopLoanServicingService();
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Create once per module and reuse as singletons.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Reactive Observables — Critical Rules
|
|
53
|
+
|
|
54
|
+
joopjs uses its own `JoopSubject` — **not RxJS**:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
// Subscribe — always returns an unsubscribe function
|
|
58
|
+
const unsub = service.event$().subscribe(value => {
|
|
59
|
+
console.log(value);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Emit
|
|
63
|
+
subject.next(newValue); // CORRECT
|
|
64
|
+
subject.emit(newValue); // WRONG — throws, does not exist
|
|
65
|
+
|
|
66
|
+
// Current value (BehaviorSubject only)
|
|
67
|
+
const val = behaviorSubject.getValue();
|
|
68
|
+
|
|
69
|
+
// Clean up
|
|
70
|
+
unsub(); // call the returned function
|
|
71
|
+
// NOT .unsubscribe() // wrong — does not exist
|
|
72
|
+
// NOT subject.pipe(map(...)) // wrong — not RxJS
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`next()` is error-isolated: if one subscriber throws, delivery to the other subscribers still proceeds. Route subscriber errors with `setSubjectErrorHandler(fn)` (default `console.error`).
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Data Type Rules — Always Follow These
|
|
80
|
+
|
|
81
|
+
| Concept | Correct type | Never use |
|
|
82
|
+
|---------|-------------|-----------|
|
|
83
|
+
| Money amount | `number` | `BigDecimal`, `Money`, `string` |
|
|
84
|
+
| Currency | `string` ISO-4217 | enum, number |
|
|
85
|
+
| Timestamp | `number` Unix epoch ms — `Date.now()` | `new Date()`, `string` |
|
|
86
|
+
| ID | `string` | `number` |
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Error Handling
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
try {
|
|
94
|
+
const result = await service.doSomething(config);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
if (err instanceof Error) {
|
|
97
|
+
console.error(err.message);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Common Code Patterns
|
|
105
|
+
|
|
106
|
+
### Digital Wallet
|
|
107
|
+
```ts
|
|
108
|
+
import { JoopDigitalWalletService } from 'joopjs';
|
|
109
|
+
|
|
110
|
+
const wallet = new JoopDigitalWalletService();
|
|
111
|
+
const w = wallet.createWallet('user-001', { currency: 'USD', label: 'Primary' });
|
|
112
|
+
wallet.topUp(w.id, 500);
|
|
113
|
+
wallet.pay(w.id, 50, 'merchant-id', 'Coffee');
|
|
114
|
+
wallet.transfer(w.id, toWalletId, 200);
|
|
115
|
+
const bal = wallet.getBalance(w.id);
|
|
116
|
+
wallet.balance$().subscribe(({ walletId, balance }) => { /* update UI */ });
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Auth + JWT
|
|
120
|
+
```ts
|
|
121
|
+
import { JoopAuthService } from 'joopjs/auth';
|
|
122
|
+
|
|
123
|
+
const auth = new JoopAuthService();
|
|
124
|
+
const session = await auth.login(email, password);
|
|
125
|
+
auth.session$().subscribe(session => { /* null = logged out */ });
|
|
126
|
+
await auth.logout(session.sessionId);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### AES-GCM Encryption
|
|
130
|
+
```ts
|
|
131
|
+
import { JoopGcmService } from 'joopjs/encryption';
|
|
132
|
+
|
|
133
|
+
const gcm = new JoopGcmService();
|
|
134
|
+
const key = await gcm.generateKey();
|
|
135
|
+
const { ciphertext, iv } = await gcm.encrypt('sensitive', key);
|
|
136
|
+
const plain = await gcm.decrypt(ciphertext, key, iv);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Loan Servicing
|
|
140
|
+
```ts
|
|
141
|
+
import { JoopLoanServicingService } from 'joopjs';
|
|
142
|
+
|
|
143
|
+
const loans = new JoopLoanServicingService();
|
|
144
|
+
const loan = loans.createLoan({
|
|
145
|
+
borrowerName: 'Alice', borrowerId: 'u-001',
|
|
146
|
+
principalAmount: 10000, annualInterestRatePercent: 8,
|
|
147
|
+
tenureMonths: 12, currency: 'USD'
|
|
148
|
+
});
|
|
149
|
+
loans.recordPayment(loan.id, loan.emiAmount);
|
|
150
|
+
const schedule = loans.getSchedule(loan.id);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Sanctions Screening
|
|
154
|
+
```ts
|
|
155
|
+
import { JoopSanctionsScreeningService } from 'joopjs';
|
|
156
|
+
|
|
157
|
+
const sanctions = new JoopSanctionsScreeningService();
|
|
158
|
+
sanctions.loadList('ofac', entities);
|
|
159
|
+
const result = sanctions.screen({ name: customerName, country: customerCountry });
|
|
160
|
+
if (result.status !== 'clear') { /* block or escalate */ }
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### AML
|
|
164
|
+
```ts
|
|
165
|
+
import { JoopAmlService } from 'joopjs';
|
|
166
|
+
|
|
167
|
+
const aml = new JoopAmlService();
|
|
168
|
+
aml.addRule({ id: 'r1', name: 'Large cash', type: 'cash', threshold: 10000, currency: 'USD', action: 'flag', enabled: true });
|
|
169
|
+
const alert = aml.checkTransaction({ id: 'tx1', amount: 15000, currency: 'USD', type: 'cash', userId: 'u-001', timestamp: Date.now() });
|
|
170
|
+
// null = passes; JoopAmlAlert = flagged
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Mutual Fund SIP
|
|
174
|
+
```ts
|
|
175
|
+
import { JoopMutualFundService } from 'joopjs';
|
|
176
|
+
|
|
177
|
+
const mf = new JoopMutualFundService();
|
|
178
|
+
mf.registerFund({ id: 'f1', name: 'Growth Fund', category: 'equity', currentNav: 45.5, currency: 'USD' });
|
|
179
|
+
mf.invest('f1', 'investor-001', 1000);
|
|
180
|
+
const sip = mf.createSip('f1', 'investor-001', 500, 'monthly', Date.now());
|
|
181
|
+
mf.executeSip(sip.id);
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Framework Integration — React
|
|
185
|
+
```tsx
|
|
186
|
+
import React, { useEffect } from 'react';
|
|
187
|
+
import { JoopDigitalWalletService } from 'joopjs';
|
|
188
|
+
|
|
189
|
+
const wallet = new JoopDigitalWalletService();
|
|
190
|
+
|
|
191
|
+
function WalletBalance() {
|
|
192
|
+
const [balance, setBalance] = React.useState(0);
|
|
193
|
+
|
|
194
|
+
useEffect(() => {
|
|
195
|
+
const unsub = wallet.balance$().subscribe(({ balance }) => setBalance(balance));
|
|
196
|
+
return unsub; // cleanup on unmount
|
|
197
|
+
}, []);
|
|
198
|
+
|
|
199
|
+
return <div>{balance}</div>;
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Framework Integration — Angular (17+ DI)
|
|
204
|
+
```ts
|
|
205
|
+
// adapter imports @angular/*; the core SDK never does
|
|
206
|
+
import { provideJoopAngular, injectJoop, joopSignal } from 'joopjs/angular';
|
|
207
|
+
bootstrapApplication(AppComponent, { providers: [...provideJoopAngular(joop)] });
|
|
208
|
+
// in a component: const joop = injectJoop(); balance = joopSignal(joop.wallet.balance$());
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
`joopjs/angular` (Angular 17+) provides real DI:
|
|
212
|
+
- `JOOP_INSTANCE` — typed `InjectionToken<JoopInstance>`; `injectJoop()` is shorthand for `inject(JOOP_INSTANCE)`.
|
|
213
|
+
- `provideJoopAngular(instance)` — bootstrap providers (replaces deprecated `provideJoop`).
|
|
214
|
+
- `joopSignal(obs, opts?)` — bridges a `JoopObservable` to an Angular `signal`, auto-teardown via `DestroyRef`. Preferred over `fromJoop` and over manual subscribe.
|
|
215
|
+
- `joopAuthGuard(opts?)` — functional `CanActivateFn` (`{ redirectTo?: string }`, default `/login`).
|
|
216
|
+
- `joopAuthInterceptor(opts?)` — `HttpInterceptorFn` attaching the access token (`{ header?, scheme? }`).
|
|
217
|
+
- Deprecated but still functional: string tokens, `provideJoop()`, `JoopModule`, `fromJoop()`. `toJoop()` is not deprecated.
|
|
218
|
+
- New optional peers: `@angular/common`, `@angular/router`.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## TypeScript Types (prefix `Joop*`)
|
|
223
|
+
|
|
224
|
+
All types are exported from `'joopjs'`:
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
// Auth
|
|
228
|
+
JoopAuthUser, JoopAuthSession, JoopAuthToken
|
|
229
|
+
|
|
230
|
+
// Banking
|
|
231
|
+
JoopWallet, JoopWalletTransaction, JoopWalletTxnType
|
|
232
|
+
JoopLoanAccount, JoopInstallment, JoopLoanAccountStatus
|
|
233
|
+
JoopFxForward, JoopForwardType, JoopForwardStatus
|
|
234
|
+
JoopLedgerAccount, JoopJournalEntry, JoopTrialBalance
|
|
235
|
+
|
|
236
|
+
// Finance
|
|
237
|
+
JoopMutualFund, JoopFundHolding, JoopSip, JoopSipStatus
|
|
238
|
+
|
|
239
|
+
// Security
|
|
240
|
+
JoopSanctionsEntity, JoopScreeningResult, JoopAmlAlert
|
|
241
|
+
```
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# GEMINI.md — JoopJS SDK
|
|
2
|
+
|
|
3
|
+
This file instructs Gemini CLI on how to generate code using the **joopjs** SDK.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Package
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install joopjs
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**joopjs** is a framework-agnostic TypeScript SDK for enterprise financial applications. Works with React, Angular, Vue, Node.js, and plain TypeScript.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Import Rules
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
// Main entry — everything
|
|
21
|
+
import { JoopAuthService, JoopDigitalWalletService } from 'joopjs';
|
|
22
|
+
|
|
23
|
+
// Sub-path imports (tree-shakeable, preferred)
|
|
24
|
+
import { JoopGcmService } from 'joopjs/encryption';
|
|
25
|
+
import { JoopAuthService } from 'joopjs/auth';
|
|
26
|
+
import { JoopCacheService } from 'joopjs/cache';
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Available sub-paths: `auth`, `encryption`, `banking`, `security`, `api`, `core`, `session`, `device`, `observability`, `theme`, `i18n`, `ui`, `forms`, `router`, `state`, `workers`, `workflow`, `sync`, `platform`, `cache`, `network`, `analytics`, `validation`, `utilities`, `pwa`, `native-bridge`, `deeplink`, `react`, `angular`, `vue`, `india`.
|
|
30
|
+
|
|
31
|
+
Never import from internal paths (`joopjs/src/...`, `joopjs/dist/...`).
|
|
32
|
+
|
|
33
|
+
`joopjs/core` exposes `globalSingleton`, `joopCopyCount`, and `silenceDuplicateCopyWarning`. In Module Federation micro-frontends, share joopjs as a singleton (`shared: { joopjs: { singleton: true } }`) so logger/config/event-bus state does not fragment across multiple loaded copies.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Service Instantiation
|
|
38
|
+
|
|
39
|
+
All services are plain classes — no DI framework required:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
const wallet = new JoopDigitalWalletService();
|
|
43
|
+
const auth = new JoopAuthService();
|
|
44
|
+
const gcm = new JoopGcmService();
|
|
45
|
+
const loans = new JoopLoanServicingService();
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Create once per module and reuse as singletons.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Reactive Observables
|
|
53
|
+
|
|
54
|
+
joopjs uses its own `JoopSubject` — **not RxJS**:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
// Subscribe — returns an unsubscribe function
|
|
58
|
+
const unsub = service.event$().subscribe(value => {
|
|
59
|
+
console.log(value);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Emit (only from inside a service or your own subject)
|
|
63
|
+
subject.next(newValue); // CORRECT
|
|
64
|
+
subject.emit(newValue); // WRONG — .emit() does not exist
|
|
65
|
+
|
|
66
|
+
// Read current value (BehaviorSubject only)
|
|
67
|
+
const current = behaviorSubject.getValue();
|
|
68
|
+
|
|
69
|
+
// Unsubscribe
|
|
70
|
+
unsub(); // call the returned function
|
|
71
|
+
// NOT .unsubscribe() // wrong — does not exist
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`next()` is error-isolated: if one subscriber throws, delivery to the other subscribers still proceeds. Route subscriber errors with `setSubjectErrorHandler(fn)` (default `console.error`).
|
|
75
|
+
|
|
76
|
+
Do not use RxJS operators (`.pipe()`, `.map()`, etc.) — these are not RxJS Observables.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Data Types
|
|
81
|
+
|
|
82
|
+
| Concept | Type | Example |
|
|
83
|
+
|---------|------|---------|
|
|
84
|
+
| Money amount | `number` | `5000.50` |
|
|
85
|
+
| Currency | `string` ISO-4217 | `'USD'`, `'AED'`, `'INR'` |
|
|
86
|
+
| Timestamp | `number` epoch ms | `Date.now()` |
|
|
87
|
+
| ID | `string` | `'u-001'` |
|
|
88
|
+
|
|
89
|
+
Never use a Money class, BigDecimal, or `Date` objects.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Error Handling
|
|
94
|
+
|
|
95
|
+
All services throw standard `Error` on invalid input:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
try {
|
|
99
|
+
const result = await service.doSomething(config);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
if (err instanceof Error) {
|
|
102
|
+
console.error(err.message);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Key Services Quick Reference
|
|
110
|
+
|
|
111
|
+
### Banking
|
|
112
|
+
```ts
|
|
113
|
+
import { JoopDigitalWalletService, JoopLoanServicingService } from 'joopjs';
|
|
114
|
+
|
|
115
|
+
const wallet = new JoopDigitalWalletService();
|
|
116
|
+
const w = wallet.createWallet('user-001', { currency: 'USD' });
|
|
117
|
+
wallet.topUp(w.id, 500);
|
|
118
|
+
wallet.pay(w.id, 50, 'merchant-id', 'Coffee');
|
|
119
|
+
wallet.balance$().subscribe(({ walletId, balance }) => { /* update UI */ });
|
|
120
|
+
|
|
121
|
+
const loans = new JoopLoanServicingService();
|
|
122
|
+
const loan = loans.createLoan({ borrowerName: 'Alice', borrowerId: 'u-001', principalAmount: 10000, annualInterestRatePercent: 8, tenureMonths: 12, currency: 'USD' });
|
|
123
|
+
loans.recordPayment(loan.id, loan.emiAmount);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Auth
|
|
127
|
+
```ts
|
|
128
|
+
import { JoopAuthService } from 'joopjs/auth';
|
|
129
|
+
|
|
130
|
+
const auth = new JoopAuthService();
|
|
131
|
+
const session = await auth.login(email, password);
|
|
132
|
+
auth.session$().subscribe(session => { /* null = logged out */ });
|
|
133
|
+
await auth.logout(session.sessionId);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Encryption
|
|
137
|
+
```ts
|
|
138
|
+
import { JoopGcmService } from 'joopjs/encryption';
|
|
139
|
+
|
|
140
|
+
const gcm = new JoopGcmService();
|
|
141
|
+
const key = await gcm.generateKey();
|
|
142
|
+
const { ciphertext, iv } = await gcm.encrypt('sensitive data', key);
|
|
143
|
+
const plain = await gcm.decrypt(ciphertext, key, iv);
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Security
|
|
147
|
+
```ts
|
|
148
|
+
import { JoopAmlService, JoopSanctionsScreeningService } from 'joopjs';
|
|
149
|
+
|
|
150
|
+
const aml = new JoopAmlService();
|
|
151
|
+
aml.addRule({ id: 'r1', name: 'Large cash', type: 'cash', threshold: 10000, currency: 'USD', action: 'flag', enabled: true });
|
|
152
|
+
const alert = aml.checkTransaction({ id: 'tx1', amount: 15000, currency: 'USD', type: 'cash', userId: 'u-001', timestamp: Date.now() });
|
|
153
|
+
|
|
154
|
+
const sanctions = new JoopSanctionsScreeningService();
|
|
155
|
+
sanctions.loadList('ofac', entities);
|
|
156
|
+
const result = sanctions.screen({ name: 'John Doe', country: 'US' });
|
|
157
|
+
// result.status: 'clear' | 'hit' | 'review'
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Framework Integrations
|
|
161
|
+
```tsx
|
|
162
|
+
// React
|
|
163
|
+
import { createJoopReact } from 'joopjs/react';
|
|
164
|
+
const { JoopProvider, useJoopAuth, useJoopTheme } = createJoopReact(React);
|
|
165
|
+
|
|
166
|
+
// Vue
|
|
167
|
+
import { createJoopVue } from 'joopjs/vue';
|
|
168
|
+
const { provideJoop, useJoopAuth } = createJoopVue(Vue);
|
|
169
|
+
|
|
170
|
+
// Angular (17+ DI — adapter imports @angular/*; the core SDK never does)
|
|
171
|
+
import { provideJoopAngular, injectJoop, joopSignal } from 'joopjs/angular';
|
|
172
|
+
bootstrapApplication(AppComponent, { providers: [...provideJoopAngular(joop)] });
|
|
173
|
+
// in a component: const joop = injectJoop(); balance = joopSignal(joop.wallet.balance$());
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`joopjs/angular` (Angular 17+) provides real DI:
|
|
177
|
+
- `JOOP_INSTANCE` — typed `InjectionToken<JoopInstance>`; `injectJoop()` is shorthand for `inject(JOOP_INSTANCE)`.
|
|
178
|
+
- `provideJoopAngular(instance)` — bootstrap providers (replaces deprecated `provideJoop`).
|
|
179
|
+
- `joopSignal(obs, opts?)` — bridges a `JoopObservable` to an Angular `signal`, auto-teardown via `DestroyRef`. Preferred over `fromJoop` and over manual subscribe.
|
|
180
|
+
- `joopAuthGuard(opts?)` — functional `CanActivateFn` (`{ redirectTo?: string }`, default `/login`).
|
|
181
|
+
- `joopAuthInterceptor(opts?)` — `HttpInterceptorFn` attaching the access token (`{ header?, scheme? }`).
|
|
182
|
+
- Deprecated but still functional: string tokens, `provideJoop()`, `JoopModule`, `fromJoop()`. `toJoop()` is not deprecated.
|
|
183
|
+
- New optional peers: `@angular/common`, `@angular/router`.
|
package/dist/ai/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
// src/events/index.ts
|
|
4
|
+
var _onListenerError = (error) => {
|
|
5
|
+
console.error("[joopjs] a subject subscriber threw during emission:", error);
|
|
6
|
+
};
|
|
4
7
|
var JoopSubject = class {
|
|
5
8
|
_listeners = [];
|
|
6
9
|
subscribe(listener) {
|
|
@@ -10,8 +13,13 @@ var JoopSubject = class {
|
|
|
10
13
|
};
|
|
11
14
|
}
|
|
12
15
|
next(value) {
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
const listeners = this._listeners.slice();
|
|
17
|
+
for (const listener of listeners) {
|
|
18
|
+
try {
|
|
19
|
+
listener(value);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
_onListenerError(error);
|
|
22
|
+
}
|
|
15
23
|
}
|
|
16
24
|
}
|
|
17
25
|
asObservable() {
|
|
@@ -32,7 +40,11 @@ var JoopBehaviorSubject = class extends JoopSubject {
|
|
|
32
40
|
super.next(value);
|
|
33
41
|
}
|
|
34
42
|
subscribe(listener) {
|
|
35
|
-
|
|
43
|
+
try {
|
|
44
|
+
listener(this._value);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
_onListenerError(error);
|
|
47
|
+
}
|
|
36
48
|
return super.subscribe(listener);
|
|
37
49
|
}
|
|
38
50
|
asObservable() {
|