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,265 @@
|
|
|
1
|
+
# /encryption — JoopJS Encryption Services
|
|
2
|
+
|
|
3
|
+
> Author: Kundan Singh
|
|
4
|
+
|
|
5
|
+
All encryption services are imported from `'joopjs'`. Uses Web Crypto API (browser) or Node.js `crypto` module — no external dependencies required for basic usage. Advanced ciphers use `@noble/ciphers` and `@noble/curves`.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## AES-GCM Symmetric Encryption
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { JoopGcmService } from 'joopjs';
|
|
13
|
+
const gcm = new JoopGcmService();
|
|
14
|
+
|
|
15
|
+
// Generate a key (256-bit)
|
|
16
|
+
const key = await gcm.generateKey(); // CryptoKey
|
|
17
|
+
|
|
18
|
+
// Encrypt
|
|
19
|
+
const encrypted = await gcm.encrypt('Hello, World!', key);
|
|
20
|
+
// { ciphertext: string, iv: string, tag: string } — all base64
|
|
21
|
+
|
|
22
|
+
// Decrypt
|
|
23
|
+
const plaintext = await gcm.decrypt(encrypted.ciphertext, key, encrypted.iv);
|
|
24
|
+
// 'Hello, World!'
|
|
25
|
+
|
|
26
|
+
// Encrypt objects
|
|
27
|
+
const enc = await gcm.encryptObject({ amount: 5000, userId: 'u-001' }, key);
|
|
28
|
+
const obj = await gcm.decryptObject(enc, key); // { amount: 5000, userId: 'u-001' }
|
|
29
|
+
|
|
30
|
+
// Key export/import for storage
|
|
31
|
+
const exported = await gcm.exportKey(key); // base64 string
|
|
32
|
+
const imported = await gcm.importKey(exported); // CryptoKey
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Use for:** Encrypting data at rest, local storage encryption, session data.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## RSA-OAEP Asymmetric Encryption
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { JoopRsaService } from 'joopjs';
|
|
43
|
+
const rsa = new JoopRsaService();
|
|
44
|
+
|
|
45
|
+
// Generate key pair (2048-bit)
|
|
46
|
+
const keyPair = await rsa.generateKeyPair();
|
|
47
|
+
// { publicKey: CryptoKey, privateKey: CryptoKey }
|
|
48
|
+
|
|
49
|
+
// Encrypt with public key
|
|
50
|
+
const encrypted = await rsa.encrypt('sensitive data', keyPair.publicKey);
|
|
51
|
+
|
|
52
|
+
// Decrypt with private key
|
|
53
|
+
const plaintext = await rsa.decrypt(encrypted, keyPair.privateKey);
|
|
54
|
+
|
|
55
|
+
// Export keys for storage/transport
|
|
56
|
+
const pubPem = await rsa.exportPublicKey(keyPair.publicKey); // PEM string
|
|
57
|
+
const privPem = await rsa.exportPrivateKey(keyPair.privateKey); // PEM string (store securely)
|
|
58
|
+
const pub2 = await rsa.importPublicKey(pubPem);
|
|
59
|
+
const priv2 = await rsa.importPrivateKey(privPem);
|
|
60
|
+
|
|
61
|
+
// Sign + verify
|
|
62
|
+
const signature = await rsa.sign('message', keyPair.privateKey);
|
|
63
|
+
const valid = await rsa.verify('message', signature, keyPair.publicKey); // true
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Use for:** Encrypting symmetric keys, signing tokens, server-to-client key exchange.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## X25519 Key Exchange (ECDH)
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { JoopX25519Service } from 'joopjs';
|
|
74
|
+
const x25519 = new JoopX25519Service();
|
|
75
|
+
|
|
76
|
+
// Both parties generate their own key pairs
|
|
77
|
+
const aliceKeys = x25519.generateKeyPair(); // { privateKey: Uint8Array, publicKey: Uint8Array }
|
|
78
|
+
const bobKeys = x25519.generateKeyPair();
|
|
79
|
+
|
|
80
|
+
// Exchange public keys, derive shared secret
|
|
81
|
+
const aliceShared = x25519.deriveSharedSecret(aliceKeys.privateKey, bobKeys.publicKey);
|
|
82
|
+
const bobShared = x25519.deriveSharedSecret(bobKeys.privateKey, aliceKeys.publicKey);
|
|
83
|
+
// aliceShared === bobShared (both Uint8Array, 32 bytes)
|
|
84
|
+
|
|
85
|
+
// Convert to AES key for actual encryption
|
|
86
|
+
const aesKey = await x25519.deriveAesKey(aliceShared); // CryptoKey for AES-GCM
|
|
87
|
+
|
|
88
|
+
// Serialise keys for transport
|
|
89
|
+
const pubHex = x25519.toHex(aliceKeys.publicKey);
|
|
90
|
+
const pubBack = x25519.fromHex(pubHex);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Use for:** Perfect forward secrecy, establishing E2E encrypted channels between parties.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## End-to-End (E2E) Encryption Flow
|
|
98
|
+
|
|
99
|
+
Combines X25519 key exchange + AES-GCM message encryption:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { JoopX25519Service, JoopGcmService } from 'joopjs';
|
|
103
|
+
const dh = new JoopX25519Service();
|
|
104
|
+
const gcm = new JoopGcmService();
|
|
105
|
+
|
|
106
|
+
// --- Setup (once per session) ---
|
|
107
|
+
const clientKeys = dh.generateKeyPair();
|
|
108
|
+
const serverPubKey = /* received from server */ new Uint8Array(32);
|
|
109
|
+
|
|
110
|
+
const sharedSecret = dh.deriveSharedSecret(clientKeys.privateKey, serverPubKey);
|
|
111
|
+
const sessionKey = await dh.deriveAesKey(sharedSecret);
|
|
112
|
+
|
|
113
|
+
// --- Per message ---
|
|
114
|
+
async function sendMessage(plaintext: string) {
|
|
115
|
+
return gcm.encrypt(plaintext, sessionKey);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function receiveMessage(ciphertext: string, iv: string) {
|
|
119
|
+
return gcm.decrypt(ciphertext, sessionKey, iv);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Hashing
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { JoopHashService } from 'joopjs';
|
|
129
|
+
const hash = new JoopHashService();
|
|
130
|
+
|
|
131
|
+
const sha256 = await hash.sha256('password123'); // hex string
|
|
132
|
+
const sha512 = await hash.sha512('password123');
|
|
133
|
+
const hmac = await hash.hmac('message', 'secret'); // HMAC-SHA256
|
|
134
|
+
|
|
135
|
+
// Password hashing (bcrypt-equivalent, using PBKDF2)
|
|
136
|
+
const hashPwd = await hash.hashPassword('mypassword'); // { hash, salt }
|
|
137
|
+
const matches = await hash.verifyPassword('mypassword', hashPwd.hash, hashPwd.salt);
|
|
138
|
+
|
|
139
|
+
// Integrity checksums
|
|
140
|
+
const checksum = await hash.checksum(dataBuffer); // SHA-256 hex
|
|
141
|
+
const isValid = await hash.verifyChecksum(dataBuffer, checksum);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Key Manager
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
import { JoopKeyManagerService } from 'joopjs';
|
|
150
|
+
const km = new JoopKeyManagerService();
|
|
151
|
+
|
|
152
|
+
// Store and retrieve keys by ID
|
|
153
|
+
await km.store('session-key', aesKey, { expiresAt: Date.now() + 3600_000 });
|
|
154
|
+
const key = await km.retrieve('session-key'); // CryptoKey | null
|
|
155
|
+
km.revoke('session-key');
|
|
156
|
+
km.revokeExpired(); // cleanup expired keys
|
|
157
|
+
|
|
158
|
+
// Key rotation
|
|
159
|
+
const newKey = await km.rotate('session-key'); // generates new, stores, returns old
|
|
160
|
+
const exists = km.has('session-key');
|
|
161
|
+
const listing = km.list(); // [{ id, type, expiresAt, metadata }]
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Secure Random
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import { JoopRandomService } from 'joopjs';
|
|
170
|
+
const rand = new JoopRandomService();
|
|
171
|
+
|
|
172
|
+
const bytes = rand.bytes(32); // Uint8Array, cryptographically secure
|
|
173
|
+
const hex = rand.hex(32); // 64-char hex string
|
|
174
|
+
const uuid = rand.uuid(); // UUID v4
|
|
175
|
+
const token = rand.token(48); // URL-safe base64 token
|
|
176
|
+
const integer = rand.integer(1, 100); // inclusive range
|
|
177
|
+
const nonce = rand.nonce(); // 16-byte hex nonce
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Field-Level Encryption (PII Protection)
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { JoopFieldEncryptionService } from 'joopjs';
|
|
186
|
+
const ffe = new JoopFieldEncryptionService();
|
|
187
|
+
|
|
188
|
+
const key = await ffe.generateKey();
|
|
189
|
+
|
|
190
|
+
// Encrypt sensitive fields in an object
|
|
191
|
+
const protected = await ffe.encryptFields(
|
|
192
|
+
{ name: 'Alice', pan: '4111111111111111', dob: '1990-01-01', email: 'alice@bank.com' },
|
|
193
|
+
['pan', 'dob'], // fields to encrypt
|
|
194
|
+
key,
|
|
195
|
+
);
|
|
196
|
+
// { name: 'Alice', pan: '<encrypted>', dob: '<encrypted>', email: 'alice@bank.com' }
|
|
197
|
+
|
|
198
|
+
const restored = await ffe.decryptFields(protected, ['pan', 'dob'], key);
|
|
199
|
+
// original object restored
|
|
200
|
+
|
|
201
|
+
// Deterministic encryption (searchable, order-preserving)
|
|
202
|
+
const detEnc = await ffe.encryptDeterministic('4111111111111111', key);
|
|
203
|
+
// same input always produces same output — can be used as index
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## TLS / Certificate Utilities
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
import { JoopCertificateService } from 'joopjs';
|
|
212
|
+
const cert = new JoopCertificateService();
|
|
213
|
+
|
|
214
|
+
const parsed = cert.parsePem(pemString); // { subject, issuer, validity, fingerprint, extensions }
|
|
215
|
+
const valid = cert.isValid(pemString); // checks expiry + basic format
|
|
216
|
+
const chain = cert.buildChain([leafPem, intermediatePem, rootPem]);
|
|
217
|
+
const pinned = cert.pin(pemString); // SHA-256 fingerprint for certificate pinning
|
|
218
|
+
const days = cert.daysUntilExpiry(pemString);
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Vault (Secret Store)
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
import { JoopVaultService } from 'joopjs';
|
|
227
|
+
const vault = new JoopVaultService({ masterKey: 'master-key-from-env' });
|
|
228
|
+
|
|
229
|
+
await vault.set('db-password', 'super-secret');
|
|
230
|
+
const secret = await vault.get('db-password'); // 'super-secret'
|
|
231
|
+
vault.delete('db-password');
|
|
232
|
+
|
|
233
|
+
// Versioned secrets
|
|
234
|
+
await vault.setVersion('api-key', 'v1', 'key-abc');
|
|
235
|
+
await vault.setVersion('api-key', 'v2', 'key-xyz');
|
|
236
|
+
const current = await vault.get('api-key'); // 'key-xyz' (latest)
|
|
237
|
+
const v1 = await vault.getVersion('api-key', 'v1'); // 'key-abc'
|
|
238
|
+
|
|
239
|
+
vault.rotate('api-key', 'key-new'); // sets new version, keeps old versions
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## What Gets Published to npm
|
|
245
|
+
|
|
246
|
+
Controlled by `"files"` in `package.json` — acts as an allowlist. Only these two are included:
|
|
247
|
+
|
|
248
|
+
| Published to npm | Never published |
|
|
249
|
+
|-----------------|----------------|
|
|
250
|
+
| `dist/` — ESM + CJS + `.d.ts` for all 34 sub-paths | `src/` — TypeScript source |
|
|
251
|
+
| `CHANGELOG.md` — release history | `tests/` — test suite |
|
|
252
|
+
| | `scripts/` — release automation |
|
|
253
|
+
| | `playground/` — Vite demo app |
|
|
254
|
+
| | `.claude/` — Claude skills (including this file) |
|
|
255
|
+
| | `.cursor/` — Cursor rules |
|
|
256
|
+
| | `.windsurf/` — Windsurf rules |
|
|
257
|
+
| | `GEMINI.md`, `AGENTS.md` — AI tool instructions |
|
|
258
|
+
| | `tsup.config.ts`, `vitest.config.ts`, `tsconfig.json` |
|
|
259
|
+
|
|
260
|
+
Source code, AI rules, and dev tooling are **never** published to npm.
|
|
261
|
+
|
|
262
|
+
Verify the tarball contents before any publish:
|
|
263
|
+
```bash
|
|
264
|
+
npm pack --dry-run
|
|
265
|
+
```
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# /finance — JoopJS Finance Services
|
|
2
|
+
|
|
3
|
+
> Author: Kundan Singh
|
|
4
|
+
|
|
5
|
+
All finance services are imported from `'joopjs'`. Instantiate as plain singletons.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Mutual Fund
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { JoopMutualFundService } from 'joopjs';
|
|
13
|
+
const mf = new JoopMutualFundService();
|
|
14
|
+
|
|
15
|
+
mf.registerFund({ id: 'EQ001', name: 'Equity Growth Fund', category: 'equity', currentNav: 45.50, currency: 'USD' });
|
|
16
|
+
mf.updateNav('EQ001', 48.00);
|
|
17
|
+
|
|
18
|
+
const holding = mf.invest('EQ001', 'inv-001', 5000);
|
|
19
|
+
// holding.units = 5000/45.50, holding.averageCostNav = 45.50
|
|
20
|
+
|
|
21
|
+
const redemption = mf.redeem('EQ001', 'inv-001', 50); // redeem 50 units
|
|
22
|
+
// redemption.proceeds, redemption.realizedGain
|
|
23
|
+
|
|
24
|
+
mf.updateNav('EQ001', 48.00); // refreshes holding.currentValue
|
|
25
|
+
const h = mf.getHolding('EQ001', 'inv-001');
|
|
26
|
+
// h.currentValue, h.unrealizedGain, h.returnPercent
|
|
27
|
+
|
|
28
|
+
// SIP (Systematic Investment Plan)
|
|
29
|
+
const sip = mf.createSip('EQ001', 'inv-001', 500, 'monthly', Date.now(), {
|
|
30
|
+
endDate: Date.now() + 365 * 86_400_000,
|
|
31
|
+
});
|
|
32
|
+
mf.executeSip(sip.id); // executes and advances nextExecutionDate
|
|
33
|
+
mf.pauseSip(sip.id); mf.resumeSip(sip.id); mf.cancelSip(sip.id);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Frequencies:** `'weekly'` | `'monthly'` | `'quarterly'`
|
|
37
|
+
**Fund categories:** `'equity'` | `'debt'` | `'hybrid'` | `'money-market'` | `'index'` | `'etf'`
|
|
38
|
+
**Key types:** `JoopMutualFund`, `JoopFundHolding`, `JoopFundRedemption`, `JoopSip`, `JoopSipStatus`, `JoopFundCategory`
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Budget Tracker
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { JoopBudgetTrackerService } from 'joopjs';
|
|
46
|
+
const budget = new JoopBudgetTrackerService();
|
|
47
|
+
|
|
48
|
+
budget.createBudget({ name: 'Monthly Budget', period: 'monthly', currency: 'USD', startDate: Date.now() });
|
|
49
|
+
budget.addCategory(budgetId, { name: 'Food', limit: 500 });
|
|
50
|
+
budget.recordSpend(budgetId, categoryId, 120, 'Groceries');
|
|
51
|
+
const report = budget.getReport(budgetId);
|
|
52
|
+
// { categories: [{ name, limit, spent, remaining, overBudget }], totalLimit, totalSpent }
|
|
53
|
+
const alerts = budget.getAlerts(budgetId, 80); // categories at >80% utilization
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Key types:** `JoopBudget`, `JoopBudgetCategory`, `JoopBudgetReport`, `JoopBudgetAlert`
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Portfolio Tracker
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { JoopPortfolioTrackerService } from 'joopjs';
|
|
64
|
+
const portfolio = new JoopPortfolioTrackerService();
|
|
65
|
+
|
|
66
|
+
portfolio.createPortfolio({ name: 'My Investments', currency: 'USD', userId: 'u-001' });
|
|
67
|
+
portfolio.addHolding(pid, { symbol: 'AAPL', name: 'Apple Inc', quantity: 10, avgCostPrice: 150, currentPrice: 175, assetClass: 'equity' });
|
|
68
|
+
portfolio.updatePrice(pid, 'AAPL', 180);
|
|
69
|
+
const summary = portfolio.getSummary(pid);
|
|
70
|
+
// { totalInvested, currentValue, totalGain, gainPercent, holdings[] }
|
|
71
|
+
portfolio.rebalance(pid, { AAPL: 60, BONDS: 40 }); // target allocation %
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Tax Calculator
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { JoopTaxCalculatorService } from 'joopjs';
|
|
80
|
+
const tax = new JoopTaxCalculatorService();
|
|
81
|
+
|
|
82
|
+
tax.setTaxProfile({ jurisdiction: 'US', filingStatus: 'single', year: 2025 });
|
|
83
|
+
tax.addSlab(5000, 0); tax.addSlab(45000, 12); tax.addSlab(95000, 22);
|
|
84
|
+
const liability = tax.calculate(75000); // { taxableIncome, taxLiability, effectiveRate, slabs }
|
|
85
|
+
const capitalGain = tax.calcCapitalGainsTax(50000, 12, 'US');
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Cash Flow Forecaster
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { JoopCashFlowForecastService } from 'joopjs';
|
|
94
|
+
const cf = new JoopCashFlowForecastService();
|
|
95
|
+
|
|
96
|
+
cf.addCashFlow({ name: 'Salary', amount: 5000, frequency: 'monthly', startDate: Date.now(), type: 'inflow' });
|
|
97
|
+
cf.addCashFlow({ name: 'Rent', amount: 1500, frequency: 'monthly', startDate: Date.now(), type: 'outflow' });
|
|
98
|
+
const forecast = cf.forecast(Date.now(), Date.now() + 90 * 86_400_000);
|
|
99
|
+
// { periods: [{ date, inflows, outflows, netCashFlow, runningBalance }], summary }
|
|
100
|
+
const summary = cf.getSummary(Date.now(), Date.now() + 90 * 86_400_000);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Expense Tracker
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { JoopExpenseTrackerService } from 'joopjs';
|
|
109
|
+
const exp = new JoopExpenseTrackerService();
|
|
110
|
+
|
|
111
|
+
exp.add({ userId: 'u-001', amount: 45.50, category: 'dining', description: 'Lunch', date: Date.now() });
|
|
112
|
+
const monthly = exp.getByPeriod('u-001', startTs, endTs);
|
|
113
|
+
const byCategory = exp.getByCategory('u-001', 'dining', startTs, endTs);
|
|
114
|
+
const totals = exp.getTotals('u-001', startTs, endTs); // { total, byCategory: Map }
|
|
115
|
+
exp.setLimit('u-001', 'dining', 300); // monthly limit
|
|
116
|
+
const limit = exp.checkLimit('u-001', 'dining'); // { limit, spent, exceeded }
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Goal Savings
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
import { JoopGoalSavingsService } from 'joopjs';
|
|
125
|
+
const goals = new JoopGoalSavingsService();
|
|
126
|
+
|
|
127
|
+
const goal = goals.create({ name: 'Emergency Fund', targetAmount: 10000, currency: 'USD', targetDate: Date.now() + 365 * 86_400_000 });
|
|
128
|
+
goals.contribute(goal.id, 500);
|
|
129
|
+
const progress = goals.getProgress(goal.id);
|
|
130
|
+
// { current, target, percent, remaining, projectedDate, onTrack }
|
|
131
|
+
const monthly = goals.getSuggestedContribution(goal.id); // amount needed per month to hit target
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Net Worth
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { JoopNetWorthService } from 'joopjs';
|
|
140
|
+
const nw = new JoopNetWorthService();
|
|
141
|
+
|
|
142
|
+
nw.addAsset({ id: 'home', name: 'House', category: 'real-estate', value: 500000, currency: 'USD' });
|
|
143
|
+
nw.addLiability({ id: 'mortgage', name: 'Mortgage', category: 'loans', balance: 350000, currency: 'USD' });
|
|
144
|
+
const snapshot = nw.getSnapshot();
|
|
145
|
+
// { totalAssets, totalLiabilities, netWorth, assetBreakdown, liabilityBreakdown }
|
|
146
|
+
nw.updateAsset('home', { value: 520000 });
|
|
147
|
+
const history = nw.getHistory(); // snapshots over time
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Investment Calculator
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
import { JoopInvestmentCalculatorService } from 'joopjs';
|
|
156
|
+
const calc = new JoopInvestmentCalculatorService();
|
|
157
|
+
|
|
158
|
+
const compound = calc.compoundInterest(10000, 8, 10); // { principal, rate, years, finalAmount, interest }
|
|
159
|
+
const sip = calc.sipReturns(5000, 12, 15); // { monthlyAmount, months, annualRate, maturityAmount }
|
|
160
|
+
const lumpsum = calc.lumpsumReturns(100000, 12, 10);
|
|
161
|
+
const emi = calc.emiCalc(500000, 8.5, 20); // { emi, totalPayment, totalInterest }
|
|
162
|
+
const rule72 = calc.rule72(8); // 9 years to double
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Financial Report Generator
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
import { JoopFinancialReportService } from 'joopjs';
|
|
171
|
+
const reports = new JoopFinancialReportService();
|
|
172
|
+
|
|
173
|
+
reports.addEntry({ type: 'income', category: 'salary', amount: 5000, date: Date.now() });
|
|
174
|
+
reports.addEntry({ type: 'expense', category: 'rent', amount: 1500, date: Date.now() });
|
|
175
|
+
const pnl = reports.getPnLStatement(startTs, endTs);
|
|
176
|
+
// { income, expenses, grossProfit, operatingExpenses, netProfit }
|
|
177
|
+
const balance = reports.getBalanceSheet();
|
|
178
|
+
const cashFlow = reports.getCashFlowStatement(startTs, endTs);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Currency Converter
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
import { JoopCurrencyConverterService } from 'joopjs';
|
|
187
|
+
const fx = new JoopCurrencyConverterService();
|
|
188
|
+
|
|
189
|
+
fx.setRate('USD', 'EUR', 0.92);
|
|
190
|
+
fx.setRate('USD', 'INR', 83.5);
|
|
191
|
+
const result = fx.convert(1000, 'USD', 'EUR'); // { amount: 920, from, to, rate, timestamp }
|
|
192
|
+
const multi = fx.convertMultiple(1000, 'USD', ['EUR', 'INR', 'GBP']);
|
|
193
|
+
const history = fx.getRateHistory('USD', 'EUR'); // historical rates
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Loan Calculator
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
import { JoopLoanCalculatorService } from 'joopjs';
|
|
202
|
+
const lc = new JoopLoanCalculatorService();
|
|
203
|
+
|
|
204
|
+
const emi = lc.calculateEmi(500000, 8.5, 20); // { emi, totalPayment, totalInterest, schedule }
|
|
205
|
+
const eligibility = lc.checkEligibility({ income: 80000, obligations: 20000, rate: 8.5, tenureYears: 20 });
|
|
206
|
+
// { eligible, maxLoanAmount, maxEmi }
|
|
207
|
+
const prepay = lc.calcPrepaymentImpact(500000, 8.5, 20, { amount: 100000, afterMonth: 24 });
|
|
208
|
+
// { newTenure, interestSaved, reducedEmi }
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Fixed Deposit
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
import { JoopFixedDepositService } from 'joopjs';
|
|
217
|
+
const fd = new JoopFixedDepositService();
|
|
218
|
+
|
|
219
|
+
const deposit = fd.create({ principal: 50000, annualRate: 6.5, tenureMonths: 12, currency: 'USD', compoundFrequency: 'quarterly' });
|
|
220
|
+
const maturity = fd.calcMaturity(deposit.id); // { maturityAmount, interestEarned, effectiveRate }
|
|
221
|
+
fd.prematureClose(deposit.id, 0.5); // 0.5% penalty
|
|
222
|
+
const due = fd.getMaturing(30 * 86_400_000); // maturing in 30 days
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## What Gets Published to npm
|
|
228
|
+
|
|
229
|
+
Controlled by `"files"` in `package.json` — acts as an allowlist. Only these two are included:
|
|
230
|
+
|
|
231
|
+
| Published to npm | Never published |
|
|
232
|
+
|-----------------|----------------|
|
|
233
|
+
| `dist/` — ESM + CJS + `.d.ts` for all 34 sub-paths | `src/` — TypeScript source |
|
|
234
|
+
| `CHANGELOG.md` — release history | `tests/` — test suite |
|
|
235
|
+
| | `scripts/` — release automation |
|
|
236
|
+
| | `playground/` — Vite demo app |
|
|
237
|
+
| | `.claude/` — Claude skills (including this file) |
|
|
238
|
+
| | `.cursor/` — Cursor rules |
|
|
239
|
+
| | `.windsurf/` — Windsurf rules |
|
|
240
|
+
| | `GEMINI.md`, `AGENTS.md` — AI tool instructions |
|
|
241
|
+
| | `tsup.config.ts`, `vitest.config.ts`, `tsconfig.json` |
|
|
242
|
+
|
|
243
|
+
Source code, AI rules, and dev tooling are **never** published to npm.
|
|
244
|
+
|
|
245
|
+
Verify the tarball contents before any publish:
|
|
246
|
+
```bash
|
|
247
|
+
npm pack --dry-run
|
|
248
|
+
```
|