joopjs 2.0.5 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/.claude/skills/auth.md +235 -0
  2. package/.claude/skills/banking.md +377 -0
  3. package/.claude/skills/encryption.md +265 -0
  4. package/.claude/skills/finance.md +248 -0
  5. package/.claude/skills/observables.md +270 -0
  6. package/.claude/skills/security.md +240 -0
  7. package/.claude/skills/setup.md +196 -0
  8. package/.cursor/rules/joopjs.mdc +150 -0
  9. package/.github/copilot-instructions.md +143 -0
  10. package/.windsurf/rules/joopjs.md +226 -0
  11. package/CHANGELOG.md +81 -0
  12. package/README.md +47 -7
  13. package/ai-rules/AGENTS.md +241 -0
  14. package/ai-rules/GEMINI.md +183 -0
  15. package/dist/ai/index.js +15 -3
  16. package/dist/ai/index.js.map +1 -1
  17. package/dist/ai/index.mjs +15 -3
  18. package/dist/ai/index.mjs.map +1 -1
  19. package/dist/analytics/index.js +10 -2
  20. package/dist/analytics/index.js.map +1 -1
  21. package/dist/analytics/index.mjs +10 -2
  22. package/dist/analytics/index.mjs.map +1 -1
  23. package/dist/angular/index.d.mts +98 -27
  24. package/dist/angular/index.d.ts +98 -27
  25. package/dist/angular/index.js +44 -0
  26. package/dist/angular/index.js.map +1 -1
  27. package/dist/angular/index.mjs +39 -1
  28. package/dist/angular/index.mjs.map +1 -1
  29. package/dist/api/index.js +15 -3
  30. package/dist/api/index.js.map +1 -1
  31. package/dist/api/index.mjs +15 -3
  32. package/dist/api/index.mjs.map +1 -1
  33. package/dist/auth/index.js +15 -3
  34. package/dist/auth/index.js.map +1 -1
  35. package/dist/auth/index.mjs +15 -3
  36. package/dist/auth/index.mjs.map +1 -1
  37. package/dist/banking/index.js +15 -3
  38. package/dist/banking/index.js.map +1 -1
  39. package/dist/banking/index.mjs +15 -3
  40. package/dist/banking/index.mjs.map +1 -1
  41. package/dist/cache/index.js +15 -3
  42. package/dist/cache/index.js.map +1 -1
  43. package/dist/cache/index.mjs +15 -3
  44. package/dist/cache/index.mjs.map +1 -1
  45. package/dist/{index-DFqEoX_l.d.ts → consent.service-CIHNtx9h.d.ts} +1 -2
  46. package/dist/{index-B_ksKpS1.d.mts → consent.service-DQ-JAEJx.d.mts} +1 -2
  47. package/dist/core/index.d.mts +34 -1
  48. package/dist/core/index.d.ts +34 -1
  49. package/dist/core/index.js +56 -5
  50. package/dist/core/index.js.map +1 -1
  51. package/dist/core/index.mjs +54 -6
  52. package/dist/core/index.mjs.map +1 -1
  53. package/dist/deeplink/index.js +15 -3
  54. package/dist/deeplink/index.js.map +1 -1
  55. package/dist/deeplink/index.mjs +15 -3
  56. package/dist/deeplink/index.mjs.map +1 -1
  57. package/dist/device/index.js +15 -3
  58. package/dist/device/index.js.map +1 -1
  59. package/dist/device/index.mjs +15 -3
  60. package/dist/device/index.mjs.map +1 -1
  61. package/dist/forms/index.js +15 -3
  62. package/dist/forms/index.js.map +1 -1
  63. package/dist/forms/index.mjs +15 -3
  64. package/dist/forms/index.mjs.map +1 -1
  65. package/dist/i18n/index.js +15 -3
  66. package/dist/i18n/index.js.map +1 -1
  67. package/dist/i18n/index.mjs +15 -3
  68. package/dist/i18n/index.mjs.map +1 -1
  69. package/dist/index.d.mts +2 -2
  70. package/dist/index.d.ts +2 -2
  71. package/dist/index.js +50 -8
  72. package/dist/index.js.map +1 -1
  73. package/dist/index.mjs +50 -8
  74. package/dist/index.mjs.map +1 -1
  75. package/dist/{joop-CA3DMeOO.d.ts → joop-Dim2yEKG.d.ts} +1 -1
  76. package/dist/{joop-Bx7Iwj5p.d.mts → joop-GkQw13f9.d.mts} +1 -1
  77. package/dist/native-bridge/index.js +10 -2
  78. package/dist/native-bridge/index.js.map +1 -1
  79. package/dist/native-bridge/index.mjs +10 -2
  80. package/dist/native-bridge/index.mjs.map +1 -1
  81. package/dist/network/index.js +15 -3
  82. package/dist/network/index.js.map +1 -1
  83. package/dist/network/index.mjs +15 -3
  84. package/dist/network/index.mjs.map +1 -1
  85. package/dist/observability/index.js +15 -3
  86. package/dist/observability/index.js.map +1 -1
  87. package/dist/observability/index.mjs +15 -3
  88. package/dist/observability/index.mjs.map +1 -1
  89. package/dist/pwa/index.js +15 -3
  90. package/dist/pwa/index.js.map +1 -1
  91. package/dist/pwa/index.mjs +15 -3
  92. package/dist/pwa/index.mjs.map +1 -1
  93. package/dist/react/index.d.mts +2 -2
  94. package/dist/react/index.d.ts +2 -2
  95. package/dist/react/index.js +15 -3
  96. package/dist/react/index.js.map +1 -1
  97. package/dist/react/index.mjs +15 -3
  98. package/dist/react/index.mjs.map +1 -1
  99. package/dist/router/index.js +15 -3
  100. package/dist/router/index.js.map +1 -1
  101. package/dist/router/index.mjs +15 -3
  102. package/dist/router/index.mjs.map +1 -1
  103. package/dist/security/index.js +15 -3
  104. package/dist/security/index.js.map +1 -1
  105. package/dist/security/index.mjs +15 -3
  106. package/dist/security/index.mjs.map +1 -1
  107. package/dist/session/index.js +15 -3
  108. package/dist/session/index.js.map +1 -1
  109. package/dist/session/index.mjs +15 -3
  110. package/dist/session/index.mjs.map +1 -1
  111. package/dist/state/index.js +15 -3
  112. package/dist/state/index.js.map +1 -1
  113. package/dist/state/index.mjs +15 -3
  114. package/dist/state/index.mjs.map +1 -1
  115. package/dist/storage/index.js +15 -3
  116. package/dist/storage/index.js.map +1 -1
  117. package/dist/storage/index.mjs +15 -3
  118. package/dist/storage/index.mjs.map +1 -1
  119. package/dist/sync/index.js +15 -3
  120. package/dist/sync/index.js.map +1 -1
  121. package/dist/sync/index.mjs +15 -3
  122. package/dist/sync/index.mjs.map +1 -1
  123. package/dist/theme/index.js +15 -3
  124. package/dist/theme/index.js.map +1 -1
  125. package/dist/theme/index.mjs +15 -3
  126. package/dist/theme/index.mjs.map +1 -1
  127. package/dist/ui/index.js +15 -3
  128. package/dist/ui/index.js.map +1 -1
  129. package/dist/ui/index.mjs +15 -3
  130. package/dist/ui/index.mjs.map +1 -1
  131. package/dist/utilities/index.js +46 -4
  132. package/dist/utilities/index.js.map +1 -1
  133. package/dist/utilities/index.mjs +46 -4
  134. package/dist/utilities/index.mjs.map +1 -1
  135. package/dist/vue/index.d.mts +2 -2
  136. package/dist/vue/index.d.ts +2 -2
  137. package/dist/vue/index.js +15 -3
  138. package/dist/vue/index.js.map +1 -1
  139. package/dist/vue/index.mjs +15 -3
  140. package/dist/vue/index.mjs.map +1 -1
  141. package/dist/workflow/index.js +15 -3
  142. package/dist/workflow/index.js.map +1 -1
  143. package/dist/workflow/index.mjs +15 -3
  144. package/dist/workflow/index.mjs.map +1 -1
  145. package/package.json +96 -32
  146. package/scripts/setup-ai.mjs +133 -0
@@ -0,0 +1,270 @@
1
+ # /observables — JoopJS Reactive Patterns
2
+
3
+ > Author: Kundan Singh
4
+
5
+ JoopJS has its own lightweight reactive primitives — `JoopSubject` and `JoopBehaviorSubject`. They are **not** RxJS Observables. Never use `.emit()`, `.pipe()`, or RxJS operators.
6
+
7
+ ---
8
+
9
+ ## Core API
10
+
11
+ ### JoopSubject — event stream (no initial value)
12
+
13
+ ```ts
14
+ import { JoopSubject } from 'joopjs';
15
+
16
+ const subject = new JoopSubject<number>();
17
+
18
+ // Subscribe
19
+ const unsub = subject.subscribe((value) => {
20
+ console.log('Got:', value);
21
+ });
22
+
23
+ // Emit
24
+ subject.next(42); // all subscribers receive 42
25
+
26
+ // Unsubscribe
27
+ unsub();
28
+
29
+ // Complete (all subscribers removed)
30
+ subject.complete();
31
+ ```
32
+
33
+ ### JoopBehaviorSubject — holds current value + emits to new subscribers immediately
34
+
35
+ ```ts
36
+ import { JoopBehaviorSubject } from 'joopjs';
37
+
38
+ const balance = new JoopBehaviorSubject<number>(0); // initial value
39
+
40
+ // New subscriber gets current value immediately
41
+ balance.subscribe(v => console.log('Balance:', v)); // logs 0 right away
42
+
43
+ balance.next(100); // logs 100
44
+
45
+ // Read current value synchronously
46
+ const current = balance.getValue(); // 100
47
+
48
+ // Check if completed
49
+ const done = balance.isCompleted();
50
+ ```
51
+
52
+ ---
53
+
54
+ ## Subscribing to Service Streams
55
+
56
+ Every JoopJS service exposes reactive streams for key state changes. Always use the returned `unsubscribe` function to avoid memory leaks.
57
+
58
+ ```ts
59
+ import { JoopDigitalWalletService } from 'joopjs';
60
+ const wallet = new JoopDigitalWalletService();
61
+
62
+ // Subscribe to balance changes
63
+ const unsub = wallet.balance$().subscribe(({ walletId, balance }) => {
64
+ updateUI(walletId, balance);
65
+ });
66
+
67
+ // On component/page destroy
68
+ unsub();
69
+ ```
70
+
71
+ ### Common service streams
72
+
73
+ ```ts
74
+ // Auth
75
+ auth.session$().subscribe(session => { /* null when logged out */ });
76
+
77
+ // Wallet
78
+ wallet.balance$().subscribe(({ walletId, balance }) => { });
79
+
80
+ // AML
81
+ aml.alert$().subscribe(alert => { });
82
+
83
+ // Fraud Detection
84
+ fraud.alert$().subscribe(result => { });
85
+
86
+ // Sanctions
87
+ sanctions.hit$().subscribe(hit => { }); // only fires on non-clear results
88
+
89
+ // Risk Engine
90
+ risk.score$().subscribe(({ score, level }) => { });
91
+
92
+ // KYC
93
+ kyc.status$().subscribe(update => { });
94
+
95
+ // MFA
96
+ mfa.status$().subscribe(event => { });
97
+
98
+ // Sessions
99
+ sessions.expired$().subscribe(session => { });
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Common Patterns
105
+
106
+ ### React — useEffect subscription
107
+
108
+ ```tsx
109
+ import { useEffect, useState } from 'react';
110
+ import { JoopDigitalWalletService } from 'joopjs';
111
+
112
+ const wallet = new JoopDigitalWalletService();
113
+
114
+ function BalanceDisplay({ walletId }: { walletId: string }) {
115
+ const [balance, setBalance] = useState(0);
116
+
117
+ useEffect(() => {
118
+ const unsub = wallet.balance$().subscribe(({ walletId: id, balance }) => {
119
+ if (id === walletId) setBalance(balance);
120
+ });
121
+ return unsub; // cleanup on unmount
122
+ }, [walletId]);
123
+
124
+ return <div>{balance}</div>;
125
+ }
126
+ ```
127
+
128
+ ### Angular — async pipe
129
+
130
+ ```ts
131
+ import { Observable } from 'rxjs'; // only needed to bridge to Angular async pipe
132
+ import { JoopDigitalWalletService } from 'joopjs';
133
+
134
+ @Component({ template: `<div>{{ balance$ | async }}</div>` })
135
+ export class BalanceComponent {
136
+ balance$ = new Observable<number>(observer => {
137
+ // bridge JoopSubject → RxJS Observable for async pipe
138
+ const unsub = wallet.balance$().subscribe(({ balance }) => observer.next(balance));
139
+ return () => unsub();
140
+ });
141
+ }
142
+ ```
143
+
144
+ ### Vue — ref + onMounted/onUnmounted
145
+
146
+ ```ts
147
+ import { ref, onMounted, onUnmounted } from 'vue';
148
+ import { JoopDigitalWalletService } from 'joopjs';
149
+
150
+ const wallet = new JoopDigitalWalletService();
151
+
152
+ export function useWalletBalance(walletId: string) {
153
+ const balance = ref(0);
154
+ let unsub: () => void;
155
+
156
+ onMounted(() => {
157
+ unsub = wallet.balance$().subscribe(({ walletId: id, b }) => {
158
+ if (id === walletId) balance.value = b;
159
+ });
160
+ });
161
+
162
+ onUnmounted(() => unsub?.());
163
+ return { balance };
164
+ }
165
+ ```
166
+
167
+ ### Vanilla TypeScript — manual lifecycle
168
+
169
+ ```ts
170
+ const wallet = new JoopDigitalWalletService();
171
+ const subscriptions: Array<() => void> = [];
172
+
173
+ function init() {
174
+ subscriptions.push(
175
+ wallet.balance$().subscribe(upd => renderBalance(upd.balance))
176
+ );
177
+ }
178
+
179
+ function destroy() {
180
+ subscriptions.forEach(unsub => unsub());
181
+ subscriptions.length = 0;
182
+ }
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Creating Your Own Subjects
188
+
189
+ ```ts
190
+ import { JoopSubject, JoopBehaviorSubject } from 'joopjs';
191
+
192
+ class MyPaymentService {
193
+ private _status$ = new JoopBehaviorSubject<'idle' | 'processing' | 'done'>('idle');
194
+
195
+ status$() { return this._status$; } // expose as read-only
196
+
197
+ async processPayment(amount: number) {
198
+ this._status$.next('processing');
199
+ await doPayment(amount);
200
+ this._status$.next('done');
201
+ }
202
+ }
203
+ ```
204
+
205
+ ---
206
+
207
+ ## Error Isolation
208
+
209
+ `JoopSubject.next()` and `JoopBehaviorSubject` replay (the immediate value sent to new subscribers) are **error-isolated**. `next()` snapshots its listener list and wraps each subscriber call in try/catch, so:
210
+
211
+ - One subscriber that throws no longer aborts delivery to the others.
212
+ - A `subscribe()` / `unsubscribe()` triggered mid-emission can't disturb the in-flight notification.
213
+
214
+ Subscriber errors are routed to a handler instead of bubbling out of `next()`. By default the handler is `console.error`. Override it globally with `setSubjectErrorHandler`:
215
+
216
+ ```ts
217
+ import { JoopSubject, setSubjectErrorHandler } from 'joopjs';
218
+
219
+ // Route subscriber errors wherever you want (default: console.error)
220
+ setSubjectErrorHandler((error) => {
221
+ errorReporter.captureException(error);
222
+ });
223
+
224
+ const subject = new JoopSubject<number>();
225
+ subject.subscribe(() => { throw new Error('boom'); }); // handled, isolated
226
+ subject.subscribe((v) => console.log('still delivered:', v));
227
+
228
+ subject.next(1); // logs 'still delivered: 1' — the throwing subscriber does not block this one
229
+ ```
230
+
231
+ This is still **not** RxJS — `.subscribe()` returns an unsubscribe function and there are no operators.
232
+
233
+ ---
234
+
235
+ ## Critical Rules
236
+
237
+ | Rule | Correct | Wrong |
238
+ |------|---------|-------|
239
+ | Emit values | `.next(value)` | `.emit(value)` |
240
+ | Read current value | `.getValue()` | `.value` |
241
+ | Stop listening | Call the returned `unsub()` function | `.unsubscribe()` (doesn't exist) |
242
+ | Add listeners | `.subscribe(callback)` | `.addListener()` |
243
+ | One-time listen | Subscribe, call `unsub()` inside the callback | `.once()` (doesn't exist) |
244
+ | Merge/combine | Compose in application code | `.pipe()` (not available) |
245
+ | Check completion | `.isCompleted()` | `.closed` |
246
+
247
+ ---
248
+
249
+ ## What Gets Published to npm
250
+
251
+ Controlled by `"files"` in `package.json` — acts as an allowlist. Only these two are included:
252
+
253
+ | Published to npm | Never published |
254
+ |-----------------|----------------|
255
+ | `dist/` — ESM + CJS + `.d.ts` for all 34 sub-paths | `src/` — TypeScript source |
256
+ | `CHANGELOG.md` — release history | `tests/` — test suite |
257
+ | | `scripts/` — release automation |
258
+ | | `playground/` — Vite demo app |
259
+ | | `.claude/` — Claude skills (including this file) |
260
+ | | `.cursor/` — Cursor rules |
261
+ | | `.windsurf/` — Windsurf rules |
262
+ | | `GEMINI.md`, `AGENTS.md` — AI tool instructions |
263
+ | | `tsup.config.ts`, `vitest.config.ts`, `tsconfig.json` |
264
+
265
+ Source code, AI rules, and dev tooling are **never** published to npm.
266
+
267
+ Verify the tarball contents before any publish:
268
+ ```bash
269
+ npm pack --dry-run
270
+ ```
@@ -0,0 +1,240 @@
1
+ # /security — JoopJS Security Services
2
+
3
+ > Author: Kundan Singh
4
+
5
+ All security services are imported from `'joopjs'`. Instantiate as plain singletons.
6
+
7
+ ---
8
+
9
+ ## Sanctions Screening
10
+
11
+ ```ts
12
+ import { JoopSanctionsScreeningService } from 'joopjs';
13
+ const sanctions = new JoopSanctionsScreeningService();
14
+
15
+ sanctions.loadList('ofac', [
16
+ { id: 'e-001', name: 'Kim Jong-un', aliases: ['Dear Leader', 'Supreme Leader'],
17
+ type: 'individual', lists: ['ofac', 'un'] },
18
+ ]);
19
+
20
+ const result = sanctions.screen({ name: 'Kim Jong-un', country: 'KP' });
21
+ // result.status: 'clear' | 'hit' | 'review'
22
+ // result.matches[]: [{ entity, matchType, score, matchedOn }]
23
+ // matchType: 'exact' | 'alias' | 'fuzzy'
24
+
25
+ sanctions.disableList('eu'); sanctions.enableList('eu');
26
+ sanctions.hit$().subscribe(hit => console.log('SANCTIONS HIT:', hit)); // fires only on non-clear
27
+
28
+ // Screen with custom threshold (default 0.85)
29
+ const strict = sanctions.screen({ name: 'John Smith' }, { threshold: 0.95 });
30
+ ```
31
+
32
+ **Key types:** `JoopSanctionsEntity`, `JoopScreeningResult`, `JoopSanctionMatch`, `JoopSanctionsList`, `JoopSanctionMatchType`
33
+
34
+ ---
35
+
36
+ ## AML (Anti-Money Laundering)
37
+
38
+ ```ts
39
+ import { JoopAmlService } from 'joopjs';
40
+ const aml = new JoopAmlService();
41
+
42
+ aml.addRule({ id: 'R01', name: 'Large Cash', type: 'threshold', threshold: 10000, currency: 'USD', action: 'flag', enabled: true });
43
+
44
+ const tx = { id: 'TXN-001', amount: 15000, currency: 'USD', type: 'cash-deposit', userId: 'u-001', timestamp: Date.now() };
45
+ const alert = aml.checkTransaction(tx);
46
+ // alert: null (pass) | JoopAmlAlert { alertId, ruleId, severity, recommendation }
47
+
48
+ aml.flag(tx.id, 'TXN-001', 'manual review required');
49
+ aml.alert$().subscribe(alert => console.log('AML ALERT:', alert)); // reactive
50
+ ```
51
+
52
+ ---
53
+
54
+ ## Risk Engine
55
+
56
+ ```ts
57
+ import { JoopRiskEngineService } from 'joopjs';
58
+ const risk = new JoopRiskEngineService();
59
+
60
+ risk.addFactor({ key: 'velocityScore', weight: 0.3, transform: (v: number) => Math.min(v / 10, 1) });
61
+ risk.addFactor({ key: 'locationScore', weight: 0.2 });
62
+ risk.addFactor({ key: 'deviceScore', weight: 0.5 });
63
+
64
+ const score = risk.evaluate({ velocityScore: 8, locationScore: 3, deviceScore: 7 });
65
+ // score.total ∈ [0,1], score.level: 'low'|'medium'|'high'|'critical'
66
+
67
+ risk.setThreshold('medium', 0.4); // adjust thresholds
68
+ risk.score$().subscribe(({ score, level }) => { });
69
+ ```
70
+
71
+ ---
72
+
73
+ ## Fraud Detection
74
+
75
+ ```ts
76
+ import { JoopFraudDetectionService } from 'joopjs';
77
+ const fraud = new JoopFraudDetectionService();
78
+
79
+ fraud.addRule({ id: 'velocity', name: 'TX Velocity', condition: (ctx) => ctx.txnCount > 5, risk: 40 });
80
+ fraud.addRule({ id: 'geo', name: 'Geo Anomaly', condition: (ctx) => ctx.countries.size > 3, risk: 60 });
81
+
82
+ const result = fraud.assess({ txnCount: 10, countries: new Set(['US', 'NG', 'RU', 'CN']), amount: 5000 });
83
+ // result.risk ∈ [0,100], result.level, result.triggeredRules[]
84
+
85
+ fraud.alert$().subscribe(alert => console.log('FRAUD:', alert));
86
+ ```
87
+
88
+ ---
89
+
90
+ ## Identity Verification (KYC)
91
+
92
+ ```ts
93
+ import { JoopIdentityVerificationService } from 'joopjs';
94
+ const kyc = new JoopIdentityVerificationService();
95
+
96
+ const submission = kyc.submit({ userId: 'u-001', documentType: 'passport', documentNumber: 'P123456', expiryDate: Date.now() + 730 * 86_400_000 });
97
+ kyc.verify(submission.id, 'approved', { notes: 'Document verified' });
98
+ const status = kyc.getStatus('u-001');
99
+ // { verificationStatus: 'verified', riskLevel, kycLevel }
100
+ kyc.status$().subscribe(update => { });
101
+ ```
102
+
103
+ ---
104
+
105
+ ## Behavioral Biometrics
106
+
107
+ ```ts
108
+ import { JoopBehavioralBiometricsService } from 'joopjs';
109
+ const bio = new JoopBehavioralBiometricsService();
110
+
111
+ bio.startSession('u-001', 'session-1');
112
+ bio.recordEvent('session-1', { type: 'keystroke', key: 'a', duration: 92, interval: 145 });
113
+ bio.recordEvent('session-1', { type: 'mouse', x: 450, y: 300, velocity: 2.3 });
114
+ bio.recordEvent('session-1', { type: 'swipe', direction: 'right', duration: 180, distance: 200 });
115
+
116
+ const analysis = bio.analyzeSession('session-1');
117
+ // analysis.anomalyScore ∈ [0,1], analysis.confidence, analysis.flags[]
118
+
119
+ bio.setBaseline('u-001', { avgKeystrokeDuration: 90, avgMouseVelocity: 2.5 });
120
+ bio.anomaly$().subscribe(({ sessionId, score, flags }) => { });
121
+ ```
122
+
123
+ ---
124
+
125
+ ## Threat Intelligence
126
+
127
+ ```ts
128
+ import { JoopThreatIntelligenceService } from 'joopjs';
129
+ const ti = new JoopThreatIntelligenceService();
130
+
131
+ ti.addIndicator({ type: 'ip', value: '192.168.1.100', threat: 'botnet', severity: 'high', confidence: 0.9 });
132
+ ti.addIndicator({ type: 'domain', value: 'evil.com', threat: 'phishing', severity: 'critical', confidence: 1.0 });
133
+
134
+ const check = ti.checkIp('192.168.1.100');
135
+ // { found: true, threat, severity, confidence } | { found: false }
136
+
137
+ const block = ti.isBlocked('domain', 'evil.com'); // boolean
138
+ ti.blockIndicator('ip', '10.0.0.1');
139
+ ti.alert$().subscribe(threat => { });
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Compliance Policy
145
+
146
+ ```ts
147
+ import { JoopCompliancePolicyService } from 'joopjs';
148
+ const compliance = new JoopCompliancePolicyService();
149
+
150
+ compliance.addPolicy({ id: 'KYC-001', name: 'Customer Verification', jurisdiction: 'US', regulation: 'BSA', requirement: 'Verify all customers above $10k threshold', enabled: true });
151
+ compliance.recordAudit({ policyId: 'KYC-001', action: 'check', outcome: 'pass', details: 'Customer ID verified', performedBy: 'system' });
152
+
153
+ const status = compliance.getPolicyStatus('KYC-001'); // { policy, lastAudit, auditCount, passRate }
154
+ const failures = compliance.getRecentFailures(7 * 86_400_000);
155
+ compliance.violation$().subscribe(v => { });
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Penetration Test Reporter
161
+
162
+ ```ts
163
+ import { JoopPenTestReporterService } from 'joopjs';
164
+ const pentest = new JoopPenTestReporterService();
165
+
166
+ const engagement = pentest.createEngagement({ name: 'Q4 2025 Assessment', target: 'Banking API', scope: ['authentication', 'data validation'] });
167
+ pentest.addFinding(engagement.id, { title: 'SQL Injection in login', severity: 'critical', cvss: 9.1, description: '...', recommendation: 'Use parameterized queries' });
168
+ const report = pentest.generateReport(engagement.id); // { executiveSummary, findings, riskMatrix }
169
+ const stats = pentest.getStats(engagement.id); // { critical, high, medium, low, info }
170
+ ```
171
+
172
+ ---
173
+
174
+ ## Vulnerability Tracker
175
+
176
+ ```ts
177
+ import { JoopVulnerabilityTrackerService } from 'joopjs';
178
+ const vulns = new JoopVulnerabilityTrackerService();
179
+
180
+ vulns.add({ id: 'CVE-2025-001', title: 'Auth bypass', severity: 'high', cvss: 8.5, affectedComponent: 'login-service', status: 'open' });
181
+ vulns.updateStatus('CVE-2025-001', 'patched', 'Applied fix in v1.2.3');
182
+ const open = vulns.getOpen(); // unresolved vulnerabilities
183
+ const overdue = vulns.getOverdue(30 * 86_400_000); // open longer than 30 days
184
+ const stats = vulns.getStats(); // { total, open, patched, bySeverity }
185
+ ```
186
+
187
+ ---
188
+
189
+ ## Security Audit Log
190
+
191
+ ```ts
192
+ import { JoopSecurityAuditService } from 'joopjs';
193
+ const audit = new JoopSecurityAuditService();
194
+
195
+ audit.log({ userId: 'u-001', action: 'login', resource: 'auth', outcome: 'success', ip: '10.0.0.1' });
196
+ audit.log({ userId: 'u-001', action: 'transfer', resource: 'wallet', outcome: 'success', amount: 1000 });
197
+ const logs = audit.query({ userId: 'u-001', from: startTs, to: endTs });
198
+ const failures = audit.getFailures('login', 24 * 3600_000); // login failures in last 24h
199
+ const suspicious = audit.getSuspiciousActivity('u-001'); // multiple failures, unusual hours
200
+ ```
201
+
202
+ ---
203
+
204
+ ## Data Masking
205
+
206
+ ```ts
207
+ import { JoopDataMaskingService } from 'joopjs';
208
+ const masking = new JoopDataMaskingService();
209
+
210
+ const masked = masking.maskPan('4111111111111111'); // '**** **** **** 1111'
211
+ const maskedEmail = masking.maskEmail('user@bank.com'); // 'u***@bank.com'
212
+ const maskedPhone = masking.maskPhone('+1-555-123-4567'); // '+1-555-***-4567'
213
+ masking.addRule({ field: 'ssn', pattern: /^\d{9}$/, mask: (v) => `***-**-${v.slice(-4)}` });
214
+ const obj = masking.maskObject({ name: 'Alice', ssn: '123456789', pan: '4111111111111111' });
215
+ ```
216
+
217
+ ---
218
+
219
+ ## What Gets Published to npm
220
+
221
+ Controlled by `"files"` in `package.json` — acts as an allowlist. Only these two are included:
222
+
223
+ | Published to npm | Never published |
224
+ |-----------------|----------------|
225
+ | `dist/` — ESM + CJS + `.d.ts` for all 34 sub-paths | `src/` — TypeScript source |
226
+ | `CHANGELOG.md` — release history | `tests/` — test suite |
227
+ | | `scripts/` — release automation |
228
+ | | `playground/` — Vite demo app |
229
+ | | `.claude/` — Claude skills (including this file) |
230
+ | | `.cursor/` — Cursor rules |
231
+ | | `.windsurf/` — Windsurf rules |
232
+ | | `GEMINI.md`, `AGENTS.md` — AI tool instructions |
233
+ | | `tsup.config.ts`, `vitest.config.ts`, `tsconfig.json` |
234
+
235
+ Source code, AI rules, and dev tooling are **never** published to npm.
236
+
237
+ Verify the tarball contents before any publish:
238
+ ```bash
239
+ npm pack --dry-run
240
+ ```
@@ -0,0 +1,196 @@
1
+ # /setup — Install and Configure JoopJS
2
+
3
+ > Author: Kundan Singh
4
+
5
+ Use this skill when a developer needs to add joopjs to a project, bootstrap it, or configure it.
6
+
7
+ ---
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install joopjs
13
+ # or
14
+ yarn add joopjs
15
+ pnpm add joopjs
16
+ ```
17
+
18
+ No peer dependencies are required for core features. React / Angular / Vue bindings are optional.
19
+
20
+ ---
21
+
22
+ ## Bootstrap
23
+
24
+ ### Plain TypeScript / Vanilla JS
25
+ ```ts
26
+ import { createJoop } from 'joopjs';
27
+
28
+ const joop = createJoop({
29
+ env: 'production', // 'development' | 'staging' | 'production'
30
+ appId: 'my-banking-app',
31
+ version: '1.0.0',
32
+ locale: 'en',
33
+ currency: 'USD',
34
+ });
35
+ ```
36
+
37
+ ### React
38
+ ```tsx
39
+ // app.tsx
40
+ import { createJoop } from 'joopjs';
41
+ export const joop = createJoop({ env: 'production', appId: 'app' });
42
+
43
+ // Use services as singletons
44
+ import { JoopAuthService } from 'joopjs';
45
+ const auth = new JoopAuthService();
46
+ ```
47
+
48
+ ### Angular (standalone)
49
+ ```ts
50
+ // main.ts
51
+ import { provideJoopAngular } from 'joopjs/angular';
52
+ import { createJoop } from 'joopjs';
53
+
54
+ bootstrapApplication(AppComponent, {
55
+ providers: [provideJoopAngular(createJoop({ env: 'production', appId: 'app' }))]
56
+ });
57
+ ```
58
+
59
+ Inject the instance with `injectJoop()` and bridge any JoopJS observable to an Angular signal with `joopSignal()` (auto-teardown via `DestroyRef`):
60
+ ```ts
61
+ import { injectJoop, joopSignal } from 'joopjs/angular';
62
+
63
+ const joop = injectJoop();
64
+ const session = joopSignal(auth.session$()); // Angular signal, no manual unsubscribe
65
+ ```
66
+
67
+ > Optional peers for the Angular adapter: `@angular/common` and `@angular/router` (only needed if you use the HTTP interceptor / guard). The core SDK never imports a framework.
68
+
69
+ ### Angular (NgModule — legacy)
70
+ ```ts
71
+ // Deprecated but still functional — prefer provideJoopAngular() above.
72
+ import { JoopModule } from 'joopjs/angular';
73
+ import { createJoop } from 'joopjs';
74
+
75
+ @NgModule({
76
+ imports: [JoopModule.forRoot(createJoop({ env: 'production', appId: 'app' }))]
77
+ })
78
+ export class AppModule {}
79
+ ```
80
+
81
+ ### Vue
82
+ ```ts
83
+ // main.ts
84
+ import { createApp } from 'vue';
85
+ import { JoopVuePlugin } from 'joopjs/vue';
86
+ import { createJoop } from 'joopjs';
87
+
88
+ createApp(App)
89
+ .use(JoopVuePlugin, createJoop({ env: 'production', appId: 'app' }))
90
+ .mount('#app');
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Configuration Reference
96
+
97
+ ```ts
98
+ interface JoopConfig {
99
+ env: 'development' | 'staging' | 'production';
100
+ appId: string;
101
+ version?: string;
102
+ locale?: string; // default: 'en'
103
+ currency?: string; // default: 'USD'
104
+ logLevel?: 'debug' | 'info' | 'warn' | 'error' | 'none';
105
+ apiBaseUrl?: string; // base URL for JoopHttpClient
106
+ timeout?: number; // HTTP timeout ms, default 30000
107
+ }
108
+ ```
109
+
110
+ ---
111
+
112
+ ## Service Instantiation Pattern
113
+
114
+ All services are plain classes — no DI required:
115
+ ```ts
116
+ import {
117
+ JoopDigitalWalletService,
118
+ JoopLoanServicingService,
119
+ JoopAmlService,
120
+ } from 'joopjs';
121
+
122
+ // Create once at module/app level (singletons)
123
+ const wallet = new JoopDigitalWalletService();
124
+ const loans = new JoopLoanServicingService();
125
+ const aml = new JoopAmlService();
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Tree-Shakeable Sub-path Imports
131
+
132
+ ```ts
133
+ import { JoopGcmService } from 'joopjs/encryption';
134
+ import { JoopAuthService } from 'joopjs/auth';
135
+ import { JoopCacheService } from 'joopjs/cache';
136
+ ```
137
+
138
+ Available sub-paths: `encryption`, `auth`, `api`, `core`, `session`, `banking`, `security`, `device`, `observability`, `theme`, `i18n`, `ui`, `native-bridge`, `deeplink`, `cache`, `network`, `analytics`, `validation`, `utilities`, `forms`, `pwa`, `router`, `ai`, `state`, `workers`, `workflow`, `sync`, `platform`, `react`, `angular`, `vue`, `india`.
139
+
140
+ ---
141
+
142
+ ## Environment-Specific Setup
143
+
144
+ ```ts
145
+ // Use JoopConfigService for runtime config
146
+ import { JoopConfigService } from 'joopjs';
147
+ const config = new JoopConfigService();
148
+ config.set('apiBaseUrl', 'https://api.mybank.com');
149
+ config.set('currency', 'AED');
150
+
151
+ // Use JoopEnvironmentService for env detection
152
+ import { JoopEnvironmentService } from 'joopjs';
153
+ const env = new JoopEnvironmentService();
154
+ env.set('production', { debug: false, apiUrl: 'https://api.prod.com' });
155
+ const profile = env.get('production'); // { debug, apiUrl }
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Health Check
161
+
162
+ ```ts
163
+ import { JoopHealthService } from 'joopjs';
164
+ const health = new JoopHealthService();
165
+ health.register('api', async () => {
166
+ const ok = await fetch('/health').then(r => r.ok);
167
+ return { name: 'api', healthy: ok };
168
+ });
169
+ const report = await health.check();
170
+ // { healthy: true, checks: [{ name: 'api', healthy: true }] }
171
+ ```
172
+
173
+ ---
174
+
175
+ ## What Gets Published to npm
176
+
177
+ Controlled by `"files"` in `package.json` — acts as an allowlist. Only these two are included:
178
+
179
+ | Published to npm | Never published |
180
+ |-----------------|----------------|
181
+ | `dist/` — ESM + CJS + `.d.ts` for all 34 sub-paths | `src/` — TypeScript source |
182
+ | `CHANGELOG.md` — release history | `tests/` — test suite |
183
+ | | `scripts/` — release automation |
184
+ | | `playground/` — Vite demo app |
185
+ | | `.claude/` — Claude skills (including this file) |
186
+ | | `.cursor/` — Cursor rules |
187
+ | | `.windsurf/` — Windsurf rules |
188
+ | | `GEMINI.md`, `AGENTS.md` — AI tool instructions |
189
+ | | `tsup.config.ts`, `vitest.config.ts`, `tsconfig.json` |
190
+
191
+ Source code, AI rules, and dev tooling are **never** published to npm.
192
+
193
+ Verify the tarball contents before any publish:
194
+ ```bash
195
+ npm pack --dry-run
196
+ ```