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
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Rules for using joopjs — enterprise financial SDK
|
|
3
|
+
globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"]
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# JoopJS SDK Rules for Cursor
|
|
8
|
+
|
|
9
|
+
**Author:** Kundan Singh
|
|
10
|
+
|
|
11
|
+
This project uses **joopjs** — a framework-agnostic TypeScript SDK for enterprise financial apps. Apply these rules whenever you generate or suggest code that involves joopjs.
|
|
12
|
+
|
|
13
|
+
## Import Rules
|
|
14
|
+
|
|
15
|
+
Always import from the package root or named sub-paths:
|
|
16
|
+
```ts
|
|
17
|
+
// Correct
|
|
18
|
+
import { JoopDigitalWalletService, JoopAuthService } from 'joopjs';
|
|
19
|
+
import { JoopGcmService } from 'joopjs/encryption';
|
|
20
|
+
|
|
21
|
+
// Wrong — never import from internal paths
|
|
22
|
+
import { JoopDigitalWalletService } from 'joopjs/src/banking/digital-wallet.service';
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
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`.
|
|
26
|
+
|
|
27
|
+
## Service Instantiation
|
|
28
|
+
|
|
29
|
+
All services are plain classes with no dependencies:
|
|
30
|
+
```ts
|
|
31
|
+
const wallet = new JoopDigitalWalletService();
|
|
32
|
+
const auth = new JoopAuthService();
|
|
33
|
+
const loans = new JoopLoanServicingService();
|
|
34
|
+
const gcm = new JoopGcmService();
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Create them once at module/app level and share as singletons. No `@Injectable`, no DI container needed.
|
|
38
|
+
|
|
39
|
+
## Observables — Critical Rules
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
// Subscribe
|
|
43
|
+
const unsub = service.someEvent$().subscribe(value => { /* handler */ });
|
|
44
|
+
|
|
45
|
+
// Emit (from inside a service or your own subject)
|
|
46
|
+
mySubject.next(value); // CORRECT
|
|
47
|
+
mySubject.emit(value); // WRONG — will throw
|
|
48
|
+
|
|
49
|
+
// Read current value (BehaviorSubject only)
|
|
50
|
+
const current = myBehaviorSubject.getValue();
|
|
51
|
+
|
|
52
|
+
// Unsubscribe
|
|
53
|
+
unsub(); // call the returned function — not .unsubscribe()
|
|
54
|
+
|
|
55
|
+
// Do NOT use RxJS operators
|
|
56
|
+
service.alert$().pipe(map(x => x)) // WRONG — not RxJS
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Data Types
|
|
60
|
+
|
|
61
|
+
- Amounts: `number` (no Money class or BigDecimal)
|
|
62
|
+
- Currencies: ISO-4217 string literal — `'USD'`, `'EUR'`, `'AED'`, `'INR'`
|
|
63
|
+
- Timestamps: Unix epoch milliseconds as `number` — `Date.now()`, never `new Date()`
|
|
64
|
+
- All IDs: `string`
|
|
65
|
+
|
|
66
|
+
## Error Handling
|
|
67
|
+
|
|
68
|
+
Services throw `Error` objects on invalid operations. Always use try/catch:
|
|
69
|
+
```ts
|
|
70
|
+
try {
|
|
71
|
+
const result = await service.doSomething();
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error(error.message);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Reference Formats
|
|
78
|
+
|
|
79
|
+
Auto-generated reference numbers follow predictable patterns:
|
|
80
|
+
- Loans: `LN-YYYY-000001`
|
|
81
|
+
- FX Forwards: `FWD-YYYY-000001`
|
|
82
|
+
- Journal Entries: `JE-YYYY-000001`
|
|
83
|
+
- Standing Orders: `SO-YYYY-000001`
|
|
84
|
+
- Remittances: `RMT-YYYY-000001`
|
|
85
|
+
|
|
86
|
+
## Framework Integration
|
|
87
|
+
|
|
88
|
+
### React
|
|
89
|
+
```tsx
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
const unsub = service.event$().subscribe(handleEvent);
|
|
92
|
+
return unsub; // cleanup on unmount
|
|
93
|
+
}, []);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Angular
|
|
97
|
+
`joopjs/angular` ships real Angular 17+ DI. Bootstrap with `provideJoopAngular(instance)`, then inject and bridge observables to signals (auto-teardown via `DestroyRef`):
|
|
98
|
+
```ts
|
|
99
|
+
const joop = injectJoop();
|
|
100
|
+
balance = joopSignal(joop.wallet.balance$()); // Angular signal
|
|
101
|
+
```
|
|
102
|
+
Also available: `joopAuthGuard()` (CanActivateFn), `joopAuthInterceptor()` (HttpInterceptorFn). Legacy `provideJoop`, `JoopModule`, `fromJoop` are deprecated but still work.
|
|
103
|
+
|
|
104
|
+
### Vue
|
|
105
|
+
```ts
|
|
106
|
+
onMounted(() => { unsub = service.event$().subscribe(handleEvent); });
|
|
107
|
+
onUnmounted(() => unsub?.());
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Key Services Quick Reference
|
|
111
|
+
|
|
112
|
+
| Use Case | Service | Key Methods |
|
|
113
|
+
|----------|---------|-------------|
|
|
114
|
+
| Digital wallet | `JoopDigitalWalletService` | `createWallet`, `topUp`, `pay`, `transfer`, `getBalance`, `balance$` |
|
|
115
|
+
| Loan management | `JoopLoanServicingService` | `createLoan`, `recordPayment`, `getSchedule`, `getOutstandingBalance` |
|
|
116
|
+
| FX forwards | `JoopFxForwardService` | `setSpotRate`, `createForward`, `settleForward`, `getMarkToMarket` |
|
|
117
|
+
| Mutual funds | `JoopMutualFundService` | `registerFund`, `invest`, `redeem`, `updateNav`, `createSip`, `executeSip` |
|
|
118
|
+
| Sanctions | `JoopSanctionsScreeningService` | `loadList`, `screen`, `hit$` |
|
|
119
|
+
| AML | `JoopAmlService` | `addRule`, `checkTransaction`, `alert$` |
|
|
120
|
+
| Auth | `JoopAuthService` | `login`, `logout`, `refresh`, `getCurrentUser`, `session$` |
|
|
121
|
+
| JWT | `JoopJwtService` | `sign`, `verify`, `decode`, `revoke` |
|
|
122
|
+
| Encryption | `JoopGcmService` | `generateKey`, `encrypt`, `decrypt` |
|
|
123
|
+
| Key exchange | `JoopX25519Service` | `generateKeyPair`, `deriveSharedSecret`, `deriveAesKey` |
|
|
124
|
+
| Double-entry | `JoopLedgerService` | `addAccount`, `postEntry`, `getBalance`, `getTrialBalance` |
|
|
125
|
+
| Reconciliation | `JoopReconciliationService` | `createSession`, `autoMatch`, `manualMatch`, `getSummary` |
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## What Gets Published to npm
|
|
130
|
+
|
|
131
|
+
Controlled by `"files"` in `package.json` — acts as an allowlist. Only these two are included:
|
|
132
|
+
|
|
133
|
+
| Published to npm | Never published |
|
|
134
|
+
|-----------------|----------------|
|
|
135
|
+
| `dist/` — ESM + CJS + `.d.ts` for all 34 sub-paths | `src/` — TypeScript source |
|
|
136
|
+
| `CHANGELOG.md` — release history | `tests/` — test suite |
|
|
137
|
+
| | `scripts/` — release automation |
|
|
138
|
+
| | `playground/` — Vite demo app |
|
|
139
|
+
| | `.claude/` — Claude skills |
|
|
140
|
+
| | `.cursor/` — these Cursor rules |
|
|
141
|
+
| | `.windsurf/` — Windsurf rules |
|
|
142
|
+
| | `GEMINI.md`, `AGENTS.md` — AI tool instructions |
|
|
143
|
+
| | `tsup.config.ts`, `vitest.config.ts`, `tsconfig.json` |
|
|
144
|
+
|
|
145
|
+
Source code, AI rules, and dev tooling are **never** published to npm.
|
|
146
|
+
|
|
147
|
+
Verify the tarball contents before any publish:
|
|
148
|
+
```bash
|
|
149
|
+
npm pack --dry-run
|
|
150
|
+
```
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# GitHub Copilot Instructions — JoopJS SDK
|
|
2
|
+
|
|
3
|
+
**Author:** Kundan Singh
|
|
4
|
+
|
|
5
|
+
This workspace uses **joopjs** (`npm install joopjs`), a framework-agnostic TypeScript SDK for enterprise financial applications.
|
|
6
|
+
|
|
7
|
+
## Always Follow These Rules
|
|
8
|
+
|
|
9
|
+
### Imports
|
|
10
|
+
- Import everything from `'joopjs'` (single entry point), or from sub-paths like `'joopjs/auth'`, `'joopjs/encryption'`
|
|
11
|
+
- Never import from internal paths like `'joopjs/src/...'` or `'joopjs/dist/...'`
|
|
12
|
+
|
|
13
|
+
### Service instantiation
|
|
14
|
+
- All services are plain classes: `const svc = new JoopXxxService();`
|
|
15
|
+
- No dependency injection or Angular `@Injectable` needed for the core SDK
|
|
16
|
+
- For Angular 17+ apps, `joopjs/angular` offers real DI: `provideJoopAngular`, `injectJoop`, `joopSignal`, `joopAuthGuard`, `joopAuthInterceptor`
|
|
17
|
+
- Create one instance per module/app (treat as singletons)
|
|
18
|
+
|
|
19
|
+
### Reactive observables
|
|
20
|
+
- Use `.subscribe(callback)` — returns an unsubscribe function, call it to stop listening
|
|
21
|
+
- Emit values with `.next(value)` — **never** `.emit(value)`
|
|
22
|
+
- Read current value with `.getValue()` on JoopBehaviorSubject
|
|
23
|
+
- Do NOT use RxJS operators (`.pipe()`, `.map()`, etc.) — these are not RxJS Observables
|
|
24
|
+
- Emission is error-isolated — one throwing subscriber does not block the others
|
|
25
|
+
|
|
26
|
+
### Data conventions
|
|
27
|
+
- Amounts: plain `number`, never a Money class
|
|
28
|
+
- Currencies: ISO-4217 string (`'USD'`, `'EUR'`, `'AED'`)
|
|
29
|
+
- Timestamps: Unix epoch milliseconds (`number`), never `Date` objects
|
|
30
|
+
- IDs: pass as `string`
|
|
31
|
+
|
|
32
|
+
### Error handling
|
|
33
|
+
- All services throw standard `Error` on invalid input
|
|
34
|
+
- Wrap service calls in `try/catch`
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Common Completions to Suggest
|
|
39
|
+
|
|
40
|
+
### Digital Wallet
|
|
41
|
+
```ts
|
|
42
|
+
import { JoopDigitalWalletService } from 'joopjs';
|
|
43
|
+
const wallet = new JoopDigitalWalletService();
|
|
44
|
+
const w = wallet.createWallet('user-001', { currency: 'USD', label: 'Primary' });
|
|
45
|
+
wallet.topUp(w.id, 500);
|
|
46
|
+
wallet.pay(w.id, 50, 'merchant-id', 'description');
|
|
47
|
+
const bal = wallet.getBalance(w.id);
|
|
48
|
+
wallet.balance$().subscribe(({ walletId, balance }) => { /* update UI */ });
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Auth + JWT
|
|
52
|
+
```ts
|
|
53
|
+
import { JoopAuthService, JoopJwtService } from 'joopjs';
|
|
54
|
+
const auth = new JoopAuthService();
|
|
55
|
+
const jwt = new JoopJwtService({ secret: process.env.JWT_SECRET!, expiryMs: 3600_000 });
|
|
56
|
+
const session = await auth.login(email, password);
|
|
57
|
+
const token = jwt.sign({ userId: session.userId, role: 'user' });
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### AES-GCM Encryption
|
|
61
|
+
```ts
|
|
62
|
+
import { JoopGcmService } from 'joopjs';
|
|
63
|
+
const gcm = new JoopGcmService();
|
|
64
|
+
const key = await gcm.generateKey();
|
|
65
|
+
const enc = await gcm.encrypt(sensitiveData, key);
|
|
66
|
+
const dec = await gcm.decrypt(enc.ciphertext, key, enc.iv);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Loan Servicing
|
|
70
|
+
```ts
|
|
71
|
+
import { JoopLoanServicingService } from 'joopjs';
|
|
72
|
+
const loans = new JoopLoanServicingService();
|
|
73
|
+
const loan = loans.createLoan({ borrowerName, borrowerId, principalAmount, annualInterestRatePercent, tenureMonths, currency: 'USD' });
|
|
74
|
+
loans.recordPayment(loan.id, loan.emiAmount);
|
|
75
|
+
const schedule = loans.getSchedule(loan.id);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Sanctions Screening
|
|
79
|
+
```ts
|
|
80
|
+
import { JoopSanctionsScreeningService } from 'joopjs';
|
|
81
|
+
const sanctions = new JoopSanctionsScreeningService();
|
|
82
|
+
sanctions.loadList('ofac', entities);
|
|
83
|
+
const result = sanctions.screen({ name: customerName, country: customerCountry });
|
|
84
|
+
if (result.status !== 'clear') { /* block or escalate */ }
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Mutual Fund SIP
|
|
88
|
+
```ts
|
|
89
|
+
import { JoopMutualFundService } from 'joopjs';
|
|
90
|
+
const mf = new JoopMutualFundService();
|
|
91
|
+
const sip = mf.createSip(fundId, investorId, monthlyAmount, 'monthly', Date.now());
|
|
92
|
+
mf.executeSip(sip.id);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## TypeScript Types (prefix `Joop*`)
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
// Auth
|
|
101
|
+
JoopAuthUser, JoopAuthSession, JoopAuthToken
|
|
102
|
+
|
|
103
|
+
// Banking
|
|
104
|
+
JoopWallet, JoopWalletTransaction, JoopWalletTxnType
|
|
105
|
+
JoopLoanAccount, JoopInstallment, JoopLoanAccountStatus
|
|
106
|
+
JoopFxForward, JoopForwardType, JoopForwardStatus
|
|
107
|
+
JoopLedgerAccount, JoopJournalEntry, JoopTrialBalance
|
|
108
|
+
|
|
109
|
+
// Finance
|
|
110
|
+
JoopMutualFund, JoopFundHolding, JoopSip, JoopSipStatus, JoopFundCategory
|
|
111
|
+
|
|
112
|
+
// Security
|
|
113
|
+
JoopSanctionsEntity, JoopScreeningResult, JoopSanctionMatchType
|
|
114
|
+
JoopAmlAlert, JoopCompliancePolicy
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
All types are exported from `'joopjs'`.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## What Gets Published to npm
|
|
122
|
+
|
|
123
|
+
Controlled by `"files"` in `package.json` — acts as an allowlist. Only these two are included:
|
|
124
|
+
|
|
125
|
+
| Published to npm | Never published |
|
|
126
|
+
|-----------------|----------------|
|
|
127
|
+
| `dist/` — ESM + CJS + `.d.ts` for all 34 sub-paths | `src/` — TypeScript source |
|
|
128
|
+
| `CHANGELOG.md` — release history | `tests/` — test suite |
|
|
129
|
+
| | `scripts/` — release automation |
|
|
130
|
+
| | `playground/` — Vite demo app |
|
|
131
|
+
| | `.claude/` — Claude skills |
|
|
132
|
+
| | `.cursor/` — Cursor rules |
|
|
133
|
+
| | `.windsurf/` — Windsurf rules |
|
|
134
|
+
| | `GEMINI.md`, `AGENTS.md` — AI tool instructions |
|
|
135
|
+
| | `.github/copilot-instructions.md` — this file |
|
|
136
|
+
| | `tsup.config.ts`, `vitest.config.ts`, `tsconfig.json` |
|
|
137
|
+
|
|
138
|
+
Source code, AI rules, and dev tooling are **never** published to npm.
|
|
139
|
+
|
|
140
|
+
Verify the tarball contents before any publish:
|
|
141
|
+
```bash
|
|
142
|
+
npm pack --dry-run
|
|
143
|
+
```
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# JoopJS SDK Rules for Windsurf Cascade
|
|
2
|
+
|
|
3
|
+
**Author:** Kundan Singh
|
|
4
|
+
|
|
5
|
+
This project uses **joopjs** — a framework-agnostic TypeScript SDK for building enterprise financial applications.
|
|
6
|
+
|
|
7
|
+
## Package Information
|
|
8
|
+
|
|
9
|
+
- **Package name:** `joopjs`
|
|
10
|
+
- **Install:** `npm install joopjs`
|
|
11
|
+
- **Version:** 2.0.0
|
|
12
|
+
- **Compatibility:** Any TypeScript/JavaScript project — React, Angular, Vue, Node.js, plain TS
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Core Conventions
|
|
17
|
+
|
|
18
|
+
### All imports come from `'joopjs'`
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import {
|
|
22
|
+
JoopAuthService,
|
|
23
|
+
JoopDigitalWalletService,
|
|
24
|
+
JoopLoanServicingService,
|
|
25
|
+
JoopMutualFundService,
|
|
26
|
+
JoopSanctionsScreeningService,
|
|
27
|
+
JoopGcmService,
|
|
28
|
+
JoopX25519Service,
|
|
29
|
+
JoopSubject,
|
|
30
|
+
JoopBehaviorSubject,
|
|
31
|
+
} from 'joopjs';
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or use sub-path imports for tree-shaking: `'joopjs/auth'`, `'joopjs/encryption'`, `'joopjs/banking'`, etc.
|
|
35
|
+
|
|
36
|
+
### Services are plain class instances
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
// Good
|
|
40
|
+
const wallet = new JoopDigitalWalletService();
|
|
41
|
+
|
|
42
|
+
// Bad — no DI framework needed
|
|
43
|
+
@Injectable() class MyService { constructor(private wallet: JoopDigitalWalletService) {} }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
For Angular 17+ apps, `joopjs/angular` provides real DI: bootstrap with `provideJoopAngular(instance)`, then `const joop = injectJoop(); balance = joopSignal(joop.wallet.balance$());` (signal with auto-teardown). The core SDK itself still imports no framework.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Reactive Pattern (Non-RxJS)
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
// Subscribe — returns unsubscribe function
|
|
54
|
+
const stop = service.change$().subscribe(value => handleChange(value));
|
|
55
|
+
|
|
56
|
+
// Emit
|
|
57
|
+
subject.next(newValue); // CORRECT
|
|
58
|
+
subject.emit(newValue); // WILL THROW — wrong method name
|
|
59
|
+
|
|
60
|
+
// Cleanup
|
|
61
|
+
stop();
|
|
62
|
+
|
|
63
|
+
// BehaviorSubject current value
|
|
64
|
+
const val = behaviorSubject.getValue();
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Emission is error-isolated: a throwing subscriber no longer blocks delivery to the others. Route subscriber errors with `setSubjectErrorHandler(fn)` (defaults to `console.error`). Still not RxJS.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Data Types
|
|
72
|
+
|
|
73
|
+
| Concept | Type | Example |
|
|
74
|
+
|---------|------|---------|
|
|
75
|
+
| Money amount | `number` | `5000.00` |
|
|
76
|
+
| Currency | `string` (ISO-4217) | `'USD'`, `'AED'` |
|
|
77
|
+
| Timestamp | `number` (epoch ms) | `Date.now()` |
|
|
78
|
+
| Duration | `number` (ms) | `7 * 86_400_000` |
|
|
79
|
+
| ID | `string` | `'u-001'` |
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Domain Services Reference
|
|
84
|
+
|
|
85
|
+
### Banking
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
// Digital Wallet
|
|
89
|
+
const w = wallet.createWallet(userId, { currency: 'USD' });
|
|
90
|
+
wallet.topUp(w.id, amount);
|
|
91
|
+
wallet.pay(w.id, amount, merchantId, description);
|
|
92
|
+
wallet.transfer(w.id, toWalletId, amount);
|
|
93
|
+
wallet.freeze(w.id) / wallet.unfreeze(w.id) / wallet.close(w.id);
|
|
94
|
+
wallet.balance$().subscribe(({ walletId, balance }) => { });
|
|
95
|
+
|
|
96
|
+
// Loans
|
|
97
|
+
const loan = loans.createLoan({ borrowerName, borrowerId, principalAmount, annualInterestRatePercent, tenureMonths, currency });
|
|
98
|
+
loans.recordPayment(loan.id, amount); // interest-first allocation
|
|
99
|
+
const schedule = loans.getSchedule(loan.id); // JoopInstallment[]
|
|
100
|
+
|
|
101
|
+
// FX Forwards
|
|
102
|
+
fx.setSpotRate('USD', 'EUR', 0.92);
|
|
103
|
+
const fwd = fx.createForward({ type: 'sell', baseCurrency, quoteCurrency, notionalAmount, contractRate, maturityDate });
|
|
104
|
+
fx.settleForward(fwd.id, spotRateAtSettlement); // returns { pnl }
|
|
105
|
+
|
|
106
|
+
// Ledger
|
|
107
|
+
ledger.addAccount(code, name, 'asset' | 'liability' | 'equity' | 'revenue' | 'expense');
|
|
108
|
+
ledger.postEntry(description, [{ accountCode, debit, credit }, ...]);
|
|
109
|
+
ledger.getTrialBalance(); // { isBalanced, totalDebits, totalCredits }
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Finance
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
// Mutual Funds
|
|
116
|
+
mf.registerFund({ id, name, category: 'equity'|'debt'|'hybrid'|'index'|'etf', currentNav, currency });
|
|
117
|
+
mf.invest(fundId, investorId, amount);
|
|
118
|
+
mf.redeem(fundId, investorId, units);
|
|
119
|
+
const sip = mf.createSip(fundId, investorId, amount, 'monthly'|'weekly'|'quarterly', startDate);
|
|
120
|
+
mf.executeSip(sip.id);
|
|
121
|
+
|
|
122
|
+
// Budget
|
|
123
|
+
budget.createBudget({ name, period: 'monthly', currency, startDate });
|
|
124
|
+
budget.addCategory(budgetId, { name, limit });
|
|
125
|
+
budget.recordSpend(budgetId, categoryId, amount, description);
|
|
126
|
+
|
|
127
|
+
// Portfolio
|
|
128
|
+
portfolio.createPortfolio({ name, currency, userId });
|
|
129
|
+
portfolio.addHolding(portfolioId, { symbol, name, quantity, avgCostPrice, currentPrice, assetClass });
|
|
130
|
+
portfolio.updatePrice(portfolioId, symbol, newPrice);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Security
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
// Sanctions Screening
|
|
137
|
+
sanctions.loadList('ofac' | 'un' | 'eu' | 'uk' | 'custom', entities);
|
|
138
|
+
const result = sanctions.screen({ name, country? });
|
|
139
|
+
// result.status: 'clear' | 'hit' | 'review'
|
|
140
|
+
// result.matches[]: [{ entity, matchType: 'exact'|'alias'|'fuzzy', score }]
|
|
141
|
+
sanctions.hit$().subscribe(hit => { }); // only fires on non-clear results
|
|
142
|
+
|
|
143
|
+
// AML
|
|
144
|
+
aml.addRule({ id, name, type, threshold, currency, action: 'flag'|'block', enabled });
|
|
145
|
+
const alert = aml.checkTransaction({ id, amount, currency, type, userId, timestamp });
|
|
146
|
+
// null = passes; JoopAmlAlert = flagged/blocked
|
|
147
|
+
|
|
148
|
+
// Risk Engine
|
|
149
|
+
risk.addFactor({ key, weight, transform? });
|
|
150
|
+
const score = risk.evaluate(signals); // { total: [0-1], level: 'low'|'medium'|'high'|'critical' }
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Auth & Encryption
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
// Auth
|
|
157
|
+
const session = await auth.login(email, password);
|
|
158
|
+
await auth.logout(session.sessionId);
|
|
159
|
+
auth.session$().subscribe(session => { /* null = logged out */ });
|
|
160
|
+
|
|
161
|
+
// JWT
|
|
162
|
+
const token = jwt.sign({ userId, role }, secret);
|
|
163
|
+
const claims = jwt.verify(token, secret); // null if invalid/expired
|
|
164
|
+
|
|
165
|
+
// AES-GCM
|
|
166
|
+
const key = await gcm.generateKey();
|
|
167
|
+
const { ciphertext, iv } = await gcm.encrypt(plaintext, key);
|
|
168
|
+
const decrypted = await gcm.decrypt(ciphertext, key, iv);
|
|
169
|
+
|
|
170
|
+
// X25519 Key Exchange
|
|
171
|
+
const keys = x25519.generateKeyPair();
|
|
172
|
+
const shared = x25519.deriveSharedSecret(myPrivKey, theirPubKey);
|
|
173
|
+
const aesKey = await x25519.deriveAesKey(shared); // use with gcm.encrypt()
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Error Handling Pattern
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
try {
|
|
182
|
+
const result = loans.createLoan({ ... });
|
|
183
|
+
} catch (err) {
|
|
184
|
+
if (err instanceof Error) {
|
|
185
|
+
// All joopjs errors are standard Error with descriptive messages
|
|
186
|
+
console.error(err.message);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## When Cascade Generates JoopJS Code
|
|
194
|
+
|
|
195
|
+
Always:
|
|
196
|
+
1. Use `new JoopXxxService()` — no DI
|
|
197
|
+
2. Call `.next(value)` to emit — never `.emit(value)`
|
|
198
|
+
3. Use `number` for amounts and timestamps
|
|
199
|
+
4. Wrap service calls in `try/catch`
|
|
200
|
+
5. Store the unsubscribe function from `.subscribe()` and call it to clean up
|
|
201
|
+
6. Use ISO-4217 strings for currency — `'USD'`, not `USD` (no enum)
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## What Gets Published to npm
|
|
206
|
+
|
|
207
|
+
Controlled by `"files"` in `package.json` — acts as an allowlist. Only these two are included:
|
|
208
|
+
|
|
209
|
+
| Published to npm | Never published |
|
|
210
|
+
|-----------------|----------------|
|
|
211
|
+
| `dist/` — ESM + CJS + `.d.ts` for all 34 sub-paths | `src/` — TypeScript source |
|
|
212
|
+
| `CHANGELOG.md` — release history | `tests/` — test suite |
|
|
213
|
+
| | `scripts/` — release automation |
|
|
214
|
+
| | `playground/` — Vite demo app |
|
|
215
|
+
| | `.claude/` — Claude skills |
|
|
216
|
+
| | `.cursor/` — Cursor rules |
|
|
217
|
+
| | `.windsurf/` — these Windsurf rules |
|
|
218
|
+
| | `GEMINI.md`, `AGENTS.md` — AI tool instructions |
|
|
219
|
+
| | `tsup.config.ts`, `vitest.config.ts`, `tsconfig.json` |
|
|
220
|
+
|
|
221
|
+
Source code, AI rules, and dev tooling are **never** published to npm.
|
|
222
|
+
|
|
223
|
+
Verify the tarball contents before any publish:
|
|
224
|
+
```bash
|
|
225
|
+
npm pack --dry-run
|
|
226
|
+
```
|
package/CHANGELOG.md
CHANGED
|
@@ -8,10 +8,45 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
## [2.1.0] — 2026-06-25 — Angular DI & Federation-Safe Core
|
|
12
|
+
|
|
13
|
+
**0 new services · 21 new tests · 2189 tests total**
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- **`joopjs/angular` — real Angular 17+ DI.** Alongside the existing string tokens (kept for back-compat):
|
|
17
|
+
- `JOOP_INSTANCE` — typed `InjectionToken<JoopInstance>`, plus `provideJoopAngular()` and `injectJoop()`.
|
|
18
|
+
- `joopSignal(obs, opts?)` — bridges a `JoopObservable` to an Angular `Signal`, seeded from the current value and torn down automatically via `DestroyRef`.
|
|
19
|
+
- `joopAuthGuard(opts?)` — functional `CanActivateFn` backed by `JoopAuthService`, returning a redirect `UrlTree` when unauthenticated.
|
|
20
|
+
- `joopAuthInterceptor(opts?)` — `HttpInterceptorFn` that attaches the access token from `JoopTokenService` (configurable header/scheme).
|
|
21
|
+
- **`joopjs/core` — federation-safe singletons.** `globalSingleton()`, `joopCopyCount()`, and `silenceDuplicateCopyWarning()`. Module-level singletons (`logger`, `configService`, `joopEventBus`, `clipboard`, `clientProfile`) now resolve through a version-keyed `globalThis` registry, so duplicate bundled copies in a micro-frontend converge on one instance instead of fragmenting state. A one-time console warning fires when more than one copy is detected.
|
|
22
|
+
- **`setSubjectErrorHandler()`** in the events module to route subscriber errors.
|
|
23
|
+
- **`scripts/check-core-boundary.mjs`** + `npm run check:boundary` — CI guard enforcing that core never imports a framework (framework imports allowed only in `angular`/`react`/`vue` sub-paths).
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- **`JoopSubject` / `JoopBehaviorSubject` emission is now error-isolated.** `next()` snapshots its listeners and wraps each in try/catch, so one throwing subscriber no longer aborts delivery to the rest, and a subscribe/unsubscribe triggered mid-emission can't disturb the in-flight notification.
|
|
27
|
+
- `@angular/common` and `@angular/router` added as **optional** peer dependencies (for `joopAuthGuard` / `joopAuthInterceptor`); `tsup` externalizes them so they are never bundled.
|
|
28
|
+
|
|
29
|
+
### Deprecated
|
|
30
|
+
- `joopjs/angular`: the string tokens (`JOOP`, `JOOP_AUTH`, …), `provideJoop()`, `JoopModule`, and `fromJoop()` are superseded by `JOOP_INSTANCE` + `provideJoopAngular()` + `joopSignal()`. They remain functional but will be removed in a future major. (`toJoop()` is **not** deprecated — it has no signal equivalent for bringing existing RxJS streams into JoopJS.)
|
|
31
|
+
|
|
32
|
+
## [2.0.6] — 2026-06-22 — Patch Release
|
|
33
|
+
|
|
34
|
+
**0 new services · 0 new tests · 2168 tests total**
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
- README version badge updated from `v2.0.5` → `v2.0.6`
|
|
38
|
+
|
|
39
|
+
### Notes
|
|
40
|
+
- Patch bump to correctly reflect the published npm version after the 2.0.5 AI Rules Setup release.
|
|
41
|
+
- All 21 smoke tests pass against the npm-published package (wallet, loans, AML, sanctions, mutual funds, GCM encryption, setup-ai files).
|
|
42
|
+
|
|
11
43
|
## Version History
|
|
12
44
|
|
|
13
45
|
| Version | Codename | New Services | Tests | Total Tests | Sub-paths Added |
|
|
14
46
|
|---|---|---|---|---|---|
|
|
47
|
+
| [2.1.0](#210--angular-di--federation-safe-core) | Angular DI & Federation-Safe Core | — | +21 | **2189** | — |
|
|
48
|
+
| [2.0.6](#206--patch-release) | Patch Release | — | — | **2168** | — |
|
|
49
|
+
| [2.0.5](#205--ai-rules-setup) | AI Rules Setup | — | — | **2168** | — |
|
|
15
50
|
| [2.0.0](#200--vue-composables) | Vue Composables | — | +39 | **1080** | `vue` |
|
|
16
51
|
| [1.9.0](#190--banking-depth) | Banking Depth | 4 | +100 | **1041** | — |
|
|
17
52
|
| [1.8.0](#180--framework-bindings) | Framework Bindings | — | +25 | 941 | `react`, `angular` |
|
|
@@ -67,6 +102,52 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|
|
67
102
|
|
|
68
103
|
---
|
|
69
104
|
|
|
105
|
+
## [2.0.5] — AI Rules Setup
|
|
106
|
+
|
|
107
|
+
**0 new services · 0 new tests · 2168 tests total · DX release**
|
|
108
|
+
|
|
109
|
+
### Added
|
|
110
|
+
|
|
111
|
+
- **`npx joopjs setup-ai`** — one-command AI assistant setup for consumers. Copies joopjs-aware rules into the user's project so that Claude Code, Cursor, Windsurf, GitHub Copilot, Gemini CLI, and Codex all understand the joopjs API out of the box.
|
|
112
|
+
|
|
113
|
+
- **`scripts/setup-ai.mjs`** — the setup script, published as the `joopjs` bin entry. Copies consumer-facing rules from `node_modules/joopjs/` to the correct locations in the user's project. Skips existing files by default; `--force` flag overwrites. Prints a post-install summary with the exact CLAUDE.md skill lines to add.
|
|
114
|
+
|
|
115
|
+
- **`ai-rules/`** — new directory included in the npm package. Contains consumer-only versions of `GEMINI.md` and `AGENTS.md` (usage guide only, no library-dev content).
|
|
116
|
+
|
|
117
|
+
- **Consumer AI rules** included in the npm package for the first time:
|
|
118
|
+
- `.claude/skills/` — 7 consumer skills: `setup`, `banking`, `finance`, `security`, `auth`, `encryption`, `observables`
|
|
119
|
+
- `.cursor/rules/joopjs.mdc` — Cursor rule (auto-applied to all `.ts` / `.tsx` / `.js` files)
|
|
120
|
+
- `.windsurf/rules/joopjs.md` — Windsurf Cascade rule
|
|
121
|
+
- `.github/copilot-instructions.md` — GitHub Copilot instructions
|
|
122
|
+
- `ai-rules/GEMINI.md` — Gemini CLI consumer guide
|
|
123
|
+
- `ai-rules/AGENTS.md` — Codex / OpenAI Agents consumer guide
|
|
124
|
+
|
|
125
|
+
- **Library-dev skills** (not published to npm) — 4 new Claude Code skills for library contributors:
|
|
126
|
+
- `/dev-build` — dev environment, build commands, tsup config, dist structure
|
|
127
|
+
- `/dev-testing` — Vitest commands, test conventions, writing tests for new services
|
|
128
|
+
- `/dev-contributing` — full checklist for adding a service or sub-path, code conventions
|
|
129
|
+
- `/dev-release` — local and CI release flow, preflight checks, CHANGELOG format
|
|
130
|
+
|
|
131
|
+
- **Cursor dev rules** (`.cursor/rules/dev-contributing.mdc`) and **Windsurf dev rules** (`.windsurf/rules/dev-contributing.md`) — development-focused rule files for library contributors using Cursor or Windsurf.
|
|
132
|
+
|
|
133
|
+
- **`GEMINI.md`** and **`AGENTS.md`** at project root — library-dev guides for Gemini CLI and Codex contributors.
|
|
134
|
+
|
|
135
|
+
### Changed
|
|
136
|
+
|
|
137
|
+
- `package.json` `"files"` — extended to include `ai-rules/`, `scripts/setup-ai.mjs`, the 7 consumer Claude skills, `.cursor/rules/joopjs.mdc`, `.windsurf/rules/joopjs.md`, and `.github/copilot-instructions.md`.
|
|
138
|
+
- `package.json` `"bin"` — added `"joopjs": "scripts/setup-ai.mjs"` enabling `npx joopjs setup-ai`.
|
|
139
|
+
- `README.md` — added **AI Assistant Setup** section immediately after Install; npm badge updated to v2.0.5.
|
|
140
|
+
|
|
141
|
+
### User workflow
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
npm install joopjs
|
|
145
|
+
npx joopjs setup-ai # first time
|
|
146
|
+
npx joopjs setup-ai --force # after upgrading joopjs
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
70
151
|
## [2.0.0] — Vue Composables
|
|
71
152
|
|
|
72
153
|
**0 new core services · 39 new tests · 1080 tests total · 90 test files · 1 new sub-path**
|