joopjs 2.0.6 → 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 (140) hide show
  1. package/.claude/skills/observables.md +28 -0
  2. package/.claude/skills/setup.md +14 -3
  3. package/.cursor/rules/joopjs.mdc +4 -5
  4. package/.github/copilot-instructions.md +3 -1
  5. package/.windsurf/rules/joopjs.md +4 -0
  6. package/CHANGELOG.md +31 -2
  7. package/README.md +19 -7
  8. package/ai-rules/AGENTS.md +21 -0
  9. package/ai-rules/GEMINI.md +17 -3
  10. package/dist/ai/index.js +15 -3
  11. package/dist/ai/index.js.map +1 -1
  12. package/dist/ai/index.mjs +15 -3
  13. package/dist/ai/index.mjs.map +1 -1
  14. package/dist/analytics/index.js +10 -2
  15. package/dist/analytics/index.js.map +1 -1
  16. package/dist/analytics/index.mjs +10 -2
  17. package/dist/analytics/index.mjs.map +1 -1
  18. package/dist/angular/index.d.mts +98 -27
  19. package/dist/angular/index.d.ts +98 -27
  20. package/dist/angular/index.js +44 -0
  21. package/dist/angular/index.js.map +1 -1
  22. package/dist/angular/index.mjs +39 -1
  23. package/dist/angular/index.mjs.map +1 -1
  24. package/dist/api/index.js +15 -3
  25. package/dist/api/index.js.map +1 -1
  26. package/dist/api/index.mjs +15 -3
  27. package/dist/api/index.mjs.map +1 -1
  28. package/dist/auth/index.js +15 -3
  29. package/dist/auth/index.js.map +1 -1
  30. package/dist/auth/index.mjs +15 -3
  31. package/dist/auth/index.mjs.map +1 -1
  32. package/dist/banking/index.js +15 -3
  33. package/dist/banking/index.js.map +1 -1
  34. package/dist/banking/index.mjs +15 -3
  35. package/dist/banking/index.mjs.map +1 -1
  36. package/dist/cache/index.js +15 -3
  37. package/dist/cache/index.js.map +1 -1
  38. package/dist/cache/index.mjs +15 -3
  39. package/dist/cache/index.mjs.map +1 -1
  40. package/dist/{index-DFqEoX_l.d.ts → consent.service-CIHNtx9h.d.ts} +1 -2
  41. package/dist/{index-B_ksKpS1.d.mts → consent.service-DQ-JAEJx.d.mts} +1 -2
  42. package/dist/core/index.d.mts +34 -1
  43. package/dist/core/index.d.ts +34 -1
  44. package/dist/core/index.js +56 -5
  45. package/dist/core/index.js.map +1 -1
  46. package/dist/core/index.mjs +54 -6
  47. package/dist/core/index.mjs.map +1 -1
  48. package/dist/deeplink/index.js +15 -3
  49. package/dist/deeplink/index.js.map +1 -1
  50. package/dist/deeplink/index.mjs +15 -3
  51. package/dist/deeplink/index.mjs.map +1 -1
  52. package/dist/device/index.js +15 -3
  53. package/dist/device/index.js.map +1 -1
  54. package/dist/device/index.mjs +15 -3
  55. package/dist/device/index.mjs.map +1 -1
  56. package/dist/forms/index.js +15 -3
  57. package/dist/forms/index.js.map +1 -1
  58. package/dist/forms/index.mjs +15 -3
  59. package/dist/forms/index.mjs.map +1 -1
  60. package/dist/i18n/index.js +15 -3
  61. package/dist/i18n/index.js.map +1 -1
  62. package/dist/i18n/index.mjs +15 -3
  63. package/dist/i18n/index.mjs.map +1 -1
  64. package/dist/index.d.mts +2 -2
  65. package/dist/index.d.ts +2 -2
  66. package/dist/index.js +50 -8
  67. package/dist/index.js.map +1 -1
  68. package/dist/index.mjs +50 -8
  69. package/dist/index.mjs.map +1 -1
  70. package/dist/{joop-CA3DMeOO.d.ts → joop-Dim2yEKG.d.ts} +1 -1
  71. package/dist/{joop-Bx7Iwj5p.d.mts → joop-GkQw13f9.d.mts} +1 -1
  72. package/dist/native-bridge/index.js +10 -2
  73. package/dist/native-bridge/index.js.map +1 -1
  74. package/dist/native-bridge/index.mjs +10 -2
  75. package/dist/native-bridge/index.mjs.map +1 -1
  76. package/dist/network/index.js +15 -3
  77. package/dist/network/index.js.map +1 -1
  78. package/dist/network/index.mjs +15 -3
  79. package/dist/network/index.mjs.map +1 -1
  80. package/dist/observability/index.js +15 -3
  81. package/dist/observability/index.js.map +1 -1
  82. package/dist/observability/index.mjs +15 -3
  83. package/dist/observability/index.mjs.map +1 -1
  84. package/dist/pwa/index.js +15 -3
  85. package/dist/pwa/index.js.map +1 -1
  86. package/dist/pwa/index.mjs +15 -3
  87. package/dist/pwa/index.mjs.map +1 -1
  88. package/dist/react/index.d.mts +2 -2
  89. package/dist/react/index.d.ts +2 -2
  90. package/dist/react/index.js +15 -3
  91. package/dist/react/index.js.map +1 -1
  92. package/dist/react/index.mjs +15 -3
  93. package/dist/react/index.mjs.map +1 -1
  94. package/dist/router/index.js +15 -3
  95. package/dist/router/index.js.map +1 -1
  96. package/dist/router/index.mjs +15 -3
  97. package/dist/router/index.mjs.map +1 -1
  98. package/dist/security/index.js +15 -3
  99. package/dist/security/index.js.map +1 -1
  100. package/dist/security/index.mjs +15 -3
  101. package/dist/security/index.mjs.map +1 -1
  102. package/dist/session/index.js +15 -3
  103. package/dist/session/index.js.map +1 -1
  104. package/dist/session/index.mjs +15 -3
  105. package/dist/session/index.mjs.map +1 -1
  106. package/dist/state/index.js +15 -3
  107. package/dist/state/index.js.map +1 -1
  108. package/dist/state/index.mjs +15 -3
  109. package/dist/state/index.mjs.map +1 -1
  110. package/dist/storage/index.js +15 -3
  111. package/dist/storage/index.js.map +1 -1
  112. package/dist/storage/index.mjs +15 -3
  113. package/dist/storage/index.mjs.map +1 -1
  114. package/dist/sync/index.js +15 -3
  115. package/dist/sync/index.js.map +1 -1
  116. package/dist/sync/index.mjs +15 -3
  117. package/dist/sync/index.mjs.map +1 -1
  118. package/dist/theme/index.js +15 -3
  119. package/dist/theme/index.js.map +1 -1
  120. package/dist/theme/index.mjs +15 -3
  121. package/dist/theme/index.mjs.map +1 -1
  122. package/dist/ui/index.js +15 -3
  123. package/dist/ui/index.js.map +1 -1
  124. package/dist/ui/index.mjs +15 -3
  125. package/dist/ui/index.mjs.map +1 -1
  126. package/dist/utilities/index.js +46 -4
  127. package/dist/utilities/index.js.map +1 -1
  128. package/dist/utilities/index.mjs +46 -4
  129. package/dist/utilities/index.mjs.map +1 -1
  130. package/dist/vue/index.d.mts +2 -2
  131. package/dist/vue/index.d.ts +2 -2
  132. package/dist/vue/index.js +15 -3
  133. package/dist/vue/index.js.map +1 -1
  134. package/dist/vue/index.mjs +15 -3
  135. package/dist/vue/index.mjs.map +1 -1
  136. package/dist/workflow/index.js +15 -3
  137. package/dist/workflow/index.js.map +1 -1
  138. package/dist/workflow/index.mjs +15 -3
  139. package/dist/workflow/index.mjs.map +1 -1
  140. package/package.json +16 -2
@@ -204,6 +204,34 @@ class MyPaymentService {
204
204
 
205
205
  ---
206
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
+
207
235
  ## Critical Rules
208
236
 
209
237
  | Rule | Correct | Wrong |
@@ -48,16 +48,27 @@ const auth = new JoopAuthService();
48
48
  ### Angular (standalone)
49
49
  ```ts
50
50
  // main.ts
51
- import { provideJoop } from 'joopjs/angular';
51
+ import { provideJoopAngular } from 'joopjs/angular';
52
52
  import { createJoop } from 'joopjs';
53
53
 
54
54
  bootstrapApplication(AppComponent, {
55
- providers: [provideJoop(createJoop({ env: 'production', appId: 'app' }))]
55
+ providers: [provideJoopAngular(createJoop({ env: 'production', appId: 'app' }))]
56
56
  });
57
57
  ```
58
58
 
59
- ### Angular (NgModule)
59
+ Inject the instance with `injectJoop()` and bridge any JoopJS observable to an Angular signal with `joopSignal()` (auto-teardown via `DestroyRef`):
60
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.
61
72
  import { JoopModule } from 'joopjs/angular';
62
73
  import { createJoop } from 'joopjs';
63
74
 
@@ -94,13 +94,12 @@ useEffect(() => {
94
94
  ```
95
95
 
96
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`):
97
98
  ```ts
98
- // Bridge to RxJS for async pipe
99
- const myObs$ = new Observable(observer => {
100
- const unsub = joopSubject.subscribe(v => observer.next(v));
101
- return () => unsub();
102
- });
99
+ const joop = injectJoop();
100
+ balance = joopSignal(joop.wallet.balance$()); // Angular signal
103
101
  ```
102
+ Also available: `joopAuthGuard()` (CanActivateFn), `joopAuthInterceptor()` (HttpInterceptorFn). Legacy `provideJoop`, `JoopModule`, `fromJoop` are deprecated but still work.
104
103
 
105
104
  ### Vue
106
105
  ```ts
@@ -12,7 +12,8 @@ This workspace uses **joopjs** (`npm install joopjs`), a framework-agnostic Type
12
12
 
13
13
  ### Service instantiation
14
14
  - All services are plain classes: `const svc = new JoopXxxService();`
15
- - No dependency injection or Angular `@Injectable` needed
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`
16
17
  - Create one instance per module/app (treat as singletons)
17
18
 
18
19
  ### Reactive observables
@@ -20,6 +21,7 @@ This workspace uses **joopjs** (`npm install joopjs`), a framework-agnostic Type
20
21
  - Emit values with `.next(value)` — **never** `.emit(value)`
21
22
  - Read current value with `.getValue()` on JoopBehaviorSubject
22
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
23
25
 
24
26
  ### Data conventions
25
27
  - Amounts: plain `number`, never a Money class
@@ -43,6 +43,8 @@ const wallet = new JoopDigitalWalletService();
43
43
  @Injectable() class MyService { constructor(private wallet: JoopDigitalWalletService) {} }
44
44
  ```
45
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
+
46
48
  ---
47
49
 
48
50
  ## Reactive Pattern (Non-RxJS)
@@ -62,6 +64,8 @@ stop();
62
64
  const val = behaviorSubject.getValue();
63
65
  ```
64
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
+
65
69
  ---
66
70
 
67
71
  ## Data Types
package/CHANGELOG.md CHANGED
@@ -8,15 +8,44 @@ Versioning follows [Semantic Versioning](https://semver.org/).
8
8
 
9
9
  ---
10
10
 
11
- ## [2.0.6] — 2026-06-22
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).
12
24
 
13
25
  ### Changed
14
- - Release 2.0.6
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).
15
42
 
16
43
  ## Version History
17
44
 
18
45
  | Version | Codename | New Services | Tests | Total Tests | Sub-paths Added |
19
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** | — |
20
49
  | [2.0.5](#205--ai-rules-setup) | AI Rules Setup | — | — | **2168** | — |
21
50
  | [2.0.0](#200--vue-composables) | Vue Composables | — | +39 | **1080** | `vue` |
22
51
  | [1.9.0](#190--banking-depth) | Banking Depth | 4 | +100 | **1041** | — |
package/README.md CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  **Enterprise-grade, framework-agnostic TypeScript SDK for financial applications.**
4
4
 
5
- [![npm version](https://img.shields.io/badge/npm-v2.0.5-blue)](https://www.npmjs.com/package/joopjs)
5
+ [![npm version](https://img.shields.io/badge/npm-v2.1.0-blue)](https://www.npmjs.com/package/joopjs)
6
6
  [![license](https://img.shields.io/badge/license-MIT-green)](./LICENSE)
7
7
  [![node](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
8
- [![tests](https://img.shields.io/badge/tests-2168%20passed-success)](#)
8
+ [![tests](https://img.shields.io/badge/tests-2189%20passed-success)](#)
9
9
  [![playground](https://img.shields.io/badge/playground-live-orange)](https://kundan-leo.github.io/JoopJs/demo/)
10
10
  [![github](https://img.shields.io/badge/github-kundan--leo%2FJoopJs-black?logo=github)](https://github.com/kundan-leo/JoopJs)
11
11
 
@@ -17,6 +17,8 @@
17
17
 
18
18
  JoopJS provides 80+ production-ready services covering banking, finance, security, authentication, and encryption — all as plain TypeScript classes with no framework dependencies. Works with **React 18+**, **Angular 17+**, **Vue 3+**, or any plain TypeScript/JavaScript project.
19
19
 
20
+ **New in 2.1.0:** real Angular 17+ DI in `joopjs/angular` (`provideJoopAngular`, `injectJoop`, `joopSignal`), federation-safe singletons in `joopjs/core` (`globalSingleton`, `joopCopyCount`, `silenceDuplicateCopyWarning`) for Module Federation micro-frontends, and error-isolated observable emission.
21
+
20
22
  ---
21
23
 
22
24
  ## Table of Contents
@@ -474,27 +476,33 @@ function BalanceCard({ walletId }: { walletId: string }) {
474
476
 
475
477
  ### Angular
476
478
 
479
+ Real Angular 17+ DI. Only the `joopjs/angular` adapter imports `@angular/*` — the core SDK stays framework-agnostic.
480
+
477
481
  ```typescript
478
482
  // app.config.ts (standalone)
479
483
  import { ApplicationConfig } from '@angular/core';
480
- import { provideJoop } from 'joopjs/angular';
484
+ import { provideJoopAngular } from 'joopjs/angular';
481
485
  import { createJoop } from 'joopjs';
482
486
 
483
487
  export const appConfig: ApplicationConfig = {
484
488
  providers: [
485
- provideJoop(createJoop({ env: 'production', appId: 'my-app' })),
489
+ ...provideJoopAngular(createJoop({ env: 'production', appId: 'my-app' })),
486
490
  ],
487
491
  };
488
492
  ```
489
493
 
490
494
  ```typescript
491
495
  // component.ts
492
- import { inject } from '@angular/core';
493
- import { JOOP_AUTH } from 'joopjs/angular';
496
+ import { injectJoop, joopSignal } from 'joopjs/angular';
497
+ import { JoopAuthService } from 'joopjs';
494
498
 
495
499
  @Component({ ... })
496
500
  export class LoginComponent {
497
- private auth = inject(JOOP_AUTH);
501
+ private joop = injectJoop(); // typed JoopInstance via JOOP_INSTANCE
502
+ private auth = new JoopAuthService();
503
+
504
+ // Bridge any JoopJS observable to an Angular signal — auto-teardown via DestroyRef
505
+ session = joopSignal(this.auth.session$());
498
506
 
499
507
  async login(email: string, password: string) {
500
508
  const session = await this.auth.login(email, password);
@@ -503,6 +511,8 @@ export class LoginComponent {
503
511
  }
504
512
  ```
505
513
 
514
+ > Granular injection tokens still exist, and `joopAuthGuard()` (a `CanActivateFn`) plus `joopAuthInterceptor()` (an `HttpInterceptorFn`) are available for routing and HTTP. `provideJoop()`, `JoopModule`, and `fromJoop()` are deprecated but still functional.
515
+
506
516
  ### Vue
507
517
 
508
518
  ```typescript
@@ -561,6 +571,8 @@ unsub();
561
571
 
562
572
  **Critical rule:** Always use `.next(value)` to emit — **never `.emit(value)`** (that method does not exist).
563
573
 
574
+ Emission is **error-isolated**: `next()` wraps each subscriber call in try/catch over a snapshot of its listeners, so one bad subscriber won't break the stream for the others. Route subscriber errors anywhere with `setSubjectErrorHandler` (default: `console.error`).
575
+
564
576
  ---
565
577
 
566
578
  ## TypeScript Types
@@ -30,6 +30,8 @@ Available sub-paths: `auth`, `encryption`, `banking`, `security`, `api`, `core`,
30
30
 
31
31
  **Never** import from internal paths (`joopjs/src/...`, `joopjs/dist/...`).
32
32
 
33
+ `joopjs/core` exposes `globalSingleton`, `joopCopyCount`, and `silenceDuplicateCopyWarning`. In Module Federation micro-frontends, share joopjs as a singleton (`shared: { joopjs: { singleton: true } }`) so logger/config/event-bus state does not fragment across multiple loaded copies.
34
+
33
35
  ---
34
36
 
35
37
  ## Service Instantiation
@@ -70,6 +72,8 @@ unsub(); // call the returned function
70
72
  // NOT subject.pipe(map(...)) // wrong — not RxJS
71
73
  ```
72
74
 
75
+ `next()` is error-isolated: if one subscriber throws, delivery to the other subscribers still proceeds. Route subscriber errors with `setSubjectErrorHandler(fn)` (default `console.error`).
76
+
73
77
  ---
74
78
 
75
79
  ## Data Type Rules — Always Follow These
@@ -196,6 +200,23 @@ function WalletBalance() {
196
200
  }
197
201
  ```
198
202
 
203
+ ### Framework Integration — Angular (17+ DI)
204
+ ```ts
205
+ // adapter imports @angular/*; the core SDK never does
206
+ import { provideJoopAngular, injectJoop, joopSignal } from 'joopjs/angular';
207
+ bootstrapApplication(AppComponent, { providers: [...provideJoopAngular(joop)] });
208
+ // in a component: const joop = injectJoop(); balance = joopSignal(joop.wallet.balance$());
209
+ ```
210
+
211
+ `joopjs/angular` (Angular 17+) provides real DI:
212
+ - `JOOP_INSTANCE` — typed `InjectionToken<JoopInstance>`; `injectJoop()` is shorthand for `inject(JOOP_INSTANCE)`.
213
+ - `provideJoopAngular(instance)` — bootstrap providers (replaces deprecated `provideJoop`).
214
+ - `joopSignal(obs, opts?)` — bridges a `JoopObservable` to an Angular `signal`, auto-teardown via `DestroyRef`. Preferred over `fromJoop` and over manual subscribe.
215
+ - `joopAuthGuard(opts?)` — functional `CanActivateFn` (`{ redirectTo?: string }`, default `/login`).
216
+ - `joopAuthInterceptor(opts?)` — `HttpInterceptorFn` attaching the access token (`{ header?, scheme? }`).
217
+ - Deprecated but still functional: string tokens, `provideJoop()`, `JoopModule`, `fromJoop()`. `toJoop()` is not deprecated.
218
+ - New optional peers: `@angular/common`, `@angular/router`.
219
+
199
220
  ---
200
221
 
201
222
  ## TypeScript Types (prefix `Joop*`)
@@ -30,6 +30,8 @@ Available sub-paths: `auth`, `encryption`, `banking`, `security`, `api`, `core`,
30
30
 
31
31
  Never import from internal paths (`joopjs/src/...`, `joopjs/dist/...`).
32
32
 
33
+ `joopjs/core` exposes `globalSingleton`, `joopCopyCount`, and `silenceDuplicateCopyWarning`. In Module Federation micro-frontends, share joopjs as a singleton (`shared: { joopjs: { singleton: true } }`) so logger/config/event-bus state does not fragment across multiple loaded copies.
34
+
33
35
  ---
34
36
 
35
37
  ## Service Instantiation
@@ -69,6 +71,8 @@ unsub(); // call the returned function
69
71
  // NOT .unsubscribe() // wrong — does not exist
70
72
  ```
71
73
 
74
+ `next()` is error-isolated: if one subscriber throws, delivery to the other subscribers still proceeds. Route subscriber errors with `setSubjectErrorHandler(fn)` (default `console.error`).
75
+
72
76
  Do not use RxJS operators (`.pipe()`, `.map()`, etc.) — these are not RxJS Observables.
73
77
 
74
78
  ---
@@ -163,7 +167,17 @@ const { JoopProvider, useJoopAuth, useJoopTheme } = createJoopReact(React);
163
167
  import { createJoopVue } from 'joopjs/vue';
164
168
  const { provideJoop, useJoopAuth } = createJoopVue(Vue);
165
169
 
166
- // Angular
167
- import { provideJoop } from 'joopjs/angular';
168
- bootstrapApplication(AppComponent, { providers: [provideJoop(joop)] });
170
+ // Angular (17+ DI — adapter imports @angular/*; the core SDK never does)
171
+ import { provideJoopAngular, injectJoop, joopSignal } from 'joopjs/angular';
172
+ bootstrapApplication(AppComponent, { providers: [...provideJoopAngular(joop)] });
173
+ // in a component: const joop = injectJoop(); balance = joopSignal(joop.wallet.balance$());
169
174
  ```
175
+
176
+ `joopjs/angular` (Angular 17+) provides real DI:
177
+ - `JOOP_INSTANCE` — typed `InjectionToken<JoopInstance>`; `injectJoop()` is shorthand for `inject(JOOP_INSTANCE)`.
178
+ - `provideJoopAngular(instance)` — bootstrap providers (replaces deprecated `provideJoop`).
179
+ - `joopSignal(obs, opts?)` — bridges a `JoopObservable` to an Angular `signal`, auto-teardown via `DestroyRef`. Preferred over `fromJoop` and over manual subscribe.
180
+ - `joopAuthGuard(opts?)` — functional `CanActivateFn` (`{ redirectTo?: string }`, default `/login`).
181
+ - `joopAuthInterceptor(opts?)` — `HttpInterceptorFn` attaching the access token (`{ header?, scheme? }`).
182
+ - Deprecated but still functional: string tokens, `provideJoop()`, `JoopModule`, `fromJoop()`. `toJoop()` is not deprecated.
183
+ - New optional peers: `@angular/common`, `@angular/router`.
package/dist/ai/index.js CHANGED
@@ -1,6 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  // src/events/index.ts
4
+ var _onListenerError = (error) => {
5
+ console.error("[joopjs] a subject subscriber threw during emission:", error);
6
+ };
4
7
  var JoopSubject = class {
5
8
  _listeners = [];
6
9
  subscribe(listener) {
@@ -10,8 +13,13 @@ var JoopSubject = class {
10
13
  };
11
14
  }
12
15
  next(value) {
13
- for (const listener of this._listeners) {
14
- listener(value);
16
+ const listeners = this._listeners.slice();
17
+ for (const listener of listeners) {
18
+ try {
19
+ listener(value);
20
+ } catch (error) {
21
+ _onListenerError(error);
22
+ }
15
23
  }
16
24
  }
17
25
  asObservable() {
@@ -32,7 +40,11 @@ var JoopBehaviorSubject = class extends JoopSubject {
32
40
  super.next(value);
33
41
  }
34
42
  subscribe(listener) {
35
- listener(this._value);
43
+ try {
44
+ listener(this._value);
45
+ } catch (error) {
46
+ _onListenerError(error);
47
+ }
36
48
  return super.subscribe(listener);
37
49
  }
38
50
  asObservable() {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/events/index.ts","../../src/ai/ai-client.ts","../../src/ai/tool-registry.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export type Unsubscribe = () => void;\nexport type Listener<T> = (value: T) => void;\n\n/**\n * Lightweight pub/sub subject — emits to all current subscribers.\n * Replaces RxJS Subject without pulling in the full RxJS dependency.\n */\nexport class JoopSubject<T> {\n private _listeners: Listener<T>[] = [];\n\n subscribe(listener: Listener<T>): Unsubscribe {\n this._listeners.push(listener);\n return () => {\n this._listeners = this._listeners.filter(l => l !== listener);\n };\n }\n\n next(value: T): void {\n for (const listener of this._listeners) {\n listener(value);\n }\n }\n\n asObservable(): JoopObservable<T> {\n return new JoopObservable<T>(listener => this.subscribe(listener));\n }\n}\n\n/**\n * Stateful subject — replays the current value to any new subscriber immediately.\n * Replaces RxJS BehaviorSubject.\n */\nexport class JoopBehaviorSubject<T> extends JoopSubject<T> {\n private _value: T;\n\n constructor(initialValue: T) {\n super();\n this._value = initialValue;\n }\n\n getValue(): T {\n return this._value;\n }\n\n override next(value: T): void {\n this._value = value;\n super.next(value);\n }\n\n override subscribe(listener: Listener<T>): Unsubscribe {\n listener(this._value);\n return super.subscribe(listener);\n }\n\n override asObservable(): JoopObservable<T> {\n return new JoopObservable<T>(listener => this.subscribe(listener));\n }\n}\n\n/**\n * Read-only observable handle returned from asObservable().\n */\nexport class JoopObservable<T> {\n constructor(private _subscribeFn: (listener: Listener<T>) => Unsubscribe) {}\n\n subscribe(listener: Listener<T>): Unsubscribe {\n return this._subscribeFn(listener);\n }\n\n /** Returns the current value without subscribing (only meaningful for BehaviorSubject-backed observables). */\n getOnce(): T | undefined {\n let result: T | undefined;\n const unsub = this.subscribe(v => { result = v; });\n unsub();\n return result;\n }\n}\n","import { JoopBehaviorSubject } from '../events';\n\nexport type JoopAiProvider = 'openai' | 'anthropic' | 'gemini' | 'ollama' | 'custom';\n\nexport interface JoopAiMessage {\n role: 'system' | 'user' | 'assistant';\n content: string;\n}\n\nexport interface JoopAiConfig {\n provider: JoopAiProvider;\n apiKey?: string;\n baseUrl?: string;\n model: string;\n maxTokens?: number;\n temperature?: number;\n systemPrompt?: string;\n timeout?: number;\n}\n\nexport interface JoopAiStreamOptions {\n stream?: boolean;\n onChunk?: (token: string) => void;\n onComplete?: (full: string) => void;\n signal?: AbortSignal;\n maxTokens?: number;\n temperature?: number;\n}\n\nexport interface JoopAiUsage {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n}\n\nconst PROVIDER_DEFAULTS: Record<JoopAiProvider, { baseUrl: string }> = {\n openai: { baseUrl: 'https://api.openai.com' },\n anthropic: { baseUrl: 'https://api.anthropic.com' },\n gemini: { baseUrl: 'https://generativelanguage.googleapis.com' },\n ollama: { baseUrl: 'http://localhost:11434' },\n custom: { baseUrl: '' },\n};\n\nexport class JoopAiClient {\n private _cfg: JoopAiConfig;\n private _usage$ = new JoopBehaviorSubject<JoopAiUsage>({ promptTokens: 0, completionTokens: 0, totalTokens: 0 });\n private _abort: AbortController | null = null;\n private _history: JoopAiMessage[] = [];\n\n constructor(config: JoopAiConfig) { this._cfg = config; }\n\n configure(patch: Partial<JoopAiConfig>): void { this._cfg = { ...this._cfg, ...patch }; }\n lastUsage(): JoopAiUsage { return this._usage$.getValue(); }\n usage$() { return this._usage$.asObservable(); }\n getHistory(): JoopAiMessage[] { return [...this._history]; }\n clearHistory(): void { this._history = []; }\n\n abort(): void { this._abort?.abort(); this._abort = null; }\n\n /** Approximate token count (4 chars ≈ 1 token) */\n estimateTokens(text: string): number { return Math.ceil(text.length / 4); }\n\n async complete(prompt: string, opts: JoopAiStreamOptions = {}): Promise<string> {\n return this.chat([{ role: 'user', content: prompt }], opts);\n }\n\n async chat(messages: JoopAiMessage[], opts: JoopAiStreamOptions = {}): Promise<string> {\n this._abort = new AbortController();\n const signal = opts.signal\n ? _combineSignals(opts.signal, this._abort.signal)\n : this._abort.signal;\n\n const allMessages: JoopAiMessage[] = [];\n if (this._cfg.systemPrompt) allMessages.push({ role: 'system', content: this._cfg.systemPrompt });\n allMessages.push(...this._history, ...messages);\n\n const result = opts.stream !== false\n ? await this._streamChat(allMessages, opts, signal)\n : await this._fetchChat(allMessages, opts, signal);\n\n // Append to history\n for (const m of messages) this._history.push(m);\n this._history.push({ role: 'assistant', content: result });\n\n opts.onComplete?.(result);\n return result;\n }\n\n private async _fetchChat(messages: JoopAiMessage[], opts: JoopAiStreamOptions, signal: AbortSignal): Promise<string> {\n const { url, headers, body } = this._buildRequest(messages, opts, false);\n const res = await fetch(url, { method: 'POST', headers, body: JSON.stringify(body), signal });\n if (!res.ok) throw new Error(`JoopAiClient: ${res.status} ${await res.text()}`);\n const json = await res.json();\n return this._extractContent(json);\n }\n\n private async _streamChat(messages: JoopAiMessage[], opts: JoopAiStreamOptions, signal: AbortSignal): Promise<string> {\n const { url, headers, body } = this._buildRequest(messages, opts, true);\n const res = await fetch(url, { method: 'POST', headers, body: JSON.stringify(body), signal });\n if (!res.ok) throw new Error(`JoopAiClient: ${res.status} ${await res.text()}`);\n\n const reader = res.body!.getReader();\n const decoder = new TextDecoder();\n let full = '';\n let buf = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n const lines = buf.split('\\n');\n buf = lines.pop() ?? '';\n for (const line of lines) {\n const token = this._parseStreamLine(line.trim());\n if (token) { full += token; opts.onChunk?.(token); }\n }\n }\n return full;\n }\n\n private _buildRequest(messages: JoopAiMessage[], opts: JoopAiStreamOptions, stream: boolean): { url: string; headers: Record<string, string>; body: unknown } {\n const { provider, apiKey, model, maxTokens, temperature } = this._cfg;\n const baseUrl = this._cfg.baseUrl ?? PROVIDER_DEFAULTS[provider].baseUrl;\n const mt = opts.maxTokens ?? maxTokens ?? 1024;\n const temp = opts.temperature ?? temperature ?? 0.7;\n\n switch (provider) {\n case 'openai':\n case 'custom':\n return {\n url: `${baseUrl}/v1/chat/completions`,\n headers: { 'Content-Type': 'application/json', ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}) },\n body: { model, messages, stream, max_tokens: mt, temperature: temp },\n };\n case 'anthropic':\n return {\n url: `${baseUrl}/v1/messages`,\n headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey ?? '', 'anthropic-version': '2023-06-01' },\n body: {\n model, stream, max_tokens: mt, temperature: temp,\n system: messages.find(m => m.role === 'system')?.content,\n messages: messages.filter(m => m.role !== 'system'),\n },\n };\n case 'gemini': {\n const contents = messages.filter(m => m.role !== 'system').map(m => ({ role: m.role === 'assistant' ? 'model' : 'user', parts: [{ text: m.content }] }));\n const path = stream ? 'streamGenerateContent?alt=sse' : 'generateContent';\n return {\n url: `${baseUrl}/v1beta/models/${model}:${path}${apiKey ? `&key=${apiKey}` : ''}`,\n headers: { 'Content-Type': 'application/json' },\n body: { contents, generationConfig: { maxOutputTokens: mt, temperature: temp } },\n };\n }\n case 'ollama':\n return {\n url: `${baseUrl}/api/chat`,\n headers: { 'Content-Type': 'application/json' },\n body: { model, messages, stream, options: { num_predict: mt, temperature: temp } },\n };\n }\n }\n\n private _parseStreamLine(line: string): string {\n if (!line.startsWith('data: ') && !line.startsWith('{')) return '';\n const raw = line.startsWith('data: ') ? line.slice(6) : line;\n if (raw === '[DONE]') return '';\n try {\n const json = JSON.parse(raw);\n // OpenAI\n const oaChoice = json?.choices?.[0];\n if (oaChoice?.delta?.content) return oaChoice.delta.content;\n // Anthropic\n if (json?.type === 'content_block_delta') return json?.delta?.text ?? '';\n // Gemini\n const gmPart = json?.candidates?.[0]?.content?.parts?.[0];\n if (gmPart?.text) return gmPart.text;\n // Ollama\n if (json?.message?.content) return json.message.content;\n } catch {}\n return '';\n }\n\n private _extractContent(json: unknown): string {\n const j = json as Record<string, unknown>;\n // OpenAI\n const oa = (j?.choices as Array<{message:{content:string}}>)?.[0]?.message?.content;\n if (oa) return oa;\n // Anthropic\n const an = (j?.content as Array<{text:string}>)?.[0]?.text;\n if (an) return an;\n // Gemini\n const gm = ((j?.candidates as Array<{content:{parts:Array<{text:string}>}}>)?.[0]?.content?.parts)?.[0]?.text;\n if (gm) return gm;\n // Ollama\n const ol = (j?.message as {content:string})?.content;\n if (ol) return ol;\n return '';\n }\n}\n\nfunction _combineSignals(...signals: AbortSignal[]): AbortSignal {\n const ctrl = new AbortController();\n for (const s of signals) s.addEventListener('abort', () => ctrl.abort(), { once: true });\n return ctrl.signal;\n}\n","export interface JoopToolParameter {\n type: 'string' | 'number' | 'boolean' | 'object' | 'array';\n description?: string;\n enum?: unknown[];\n items?: JoopToolParameter;\n properties?: Record<string, JoopToolParameter>;\n required?: string[];\n}\n\nexport interface JoopToolDefinition {\n name: string;\n description: string;\n parameters: Record<string, JoopToolParameter>;\n required?: string[];\n handler: (args: Record<string, unknown>) => Promise<unknown>;\n}\n\nexport interface JoopToolCall {\n name: string;\n arguments: string | Record<string, unknown>;\n}\n\nexport interface JoopToolResult {\n name: string;\n result: unknown;\n error?: string;\n durationMs: number;\n}\n\nexport class JoopToolRegistry {\n private _tools = new Map<string, JoopToolDefinition>();\n\n register(tool: JoopToolDefinition): void {\n if (this._tools.has(tool.name)) throw new Error(`Tool '${tool.name}' already registered. Use update() to replace.`);\n this._tools.set(tool.name, tool);\n }\n\n update(tool: JoopToolDefinition): void { this._tools.set(tool.name, tool); }\n unregister(name: string): void { this._tools.delete(name); }\n has(name: string): boolean { return this._tools.has(name); }\n list(): string[] { return Array.from(this._tools.keys()); }\n get(name: string): JoopToolDefinition | undefined { return this._tools.get(name); }\n clear(): void { this._tools.clear(); }\n\n async dispatch(call: JoopToolCall): Promise<JoopToolResult> {\n const tool = this._tools.get(call.name);\n if (!tool) return { name: call.name, result: null, error: `Unknown tool: ${call.name}`, durationMs: 0 };\n\n const args = typeof call.arguments === 'string' ? JSON.parse(call.arguments) : call.arguments;\n const t0 = Date.now();\n try {\n const result = await tool.handler(args as Record<string, unknown>);\n return { name: call.name, result, durationMs: Date.now() - t0 };\n } catch (e) {\n return { name: call.name, result: null, error: String(e), durationMs: Date.now() - t0 };\n }\n }\n\n /** Dispatch multiple tool calls in parallel */\n async dispatchAll(calls: JoopToolCall[]): Promise<JoopToolResult[]> {\n return Promise.all(calls.map(c => this.dispatch(c)));\n }\n\n /** Format for OpenAI function-calling */\n toOpenAiFormat(): object[] {\n return Array.from(this._tools.values()).map(t => ({\n type: 'function',\n function: {\n name: t.name,\n description: t.description,\n parameters: {\n type: 'object',\n properties: t.parameters,\n required: t.required ?? [],\n },\n },\n }));\n }\n\n /** Format for Anthropic tool-use */\n toAnthropicFormat(): object[] {\n return Array.from(this._tools.values()).map(t => ({\n name: t.name,\n description: t.description,\n input_schema: {\n type: 'object',\n properties: t.parameters,\n required: t.required ?? [],\n },\n }));\n }\n\n /** Format for Google Gemini function declarations */\n toGeminiFormat(): object[] {\n return [{\n function_declarations: Array.from(this._tools.values()).map(t => ({\n name: t.name,\n description: t.description,\n parameters: { type: 'object', properties: t.parameters, required: t.required ?? [] },\n })),\n }];\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/events/index.ts","../../src/ai/ai-client.ts","../../src/ai/tool-registry.ts"],"names":[],"mappings":";;;AAQA,IAAI,gBAAA,GAA6C,CAAC,KAAA,KAAU;AAE1D,EAAA,OAAA,CAAQ,KAAA,CAAM,wDAAwD,KAAK,CAAA;AAC7E,CAAA;AAWO,IAAM,cAAN,MAAqB;AAAA,EAClB,aAA4B,EAAC;AAAA,EAErC,UAAU,QAAA,EAAoC;AAC5C,IAAA,IAAA,CAAK,UAAA,CAAW,KAAK,QAAQ,CAAA;AAC7B,IAAA,OAAO,MAAM;AACX,MAAA,IAAA,CAAK,aAAa,IAAA,CAAK,UAAA,CAAW,MAAA,CAAO,CAAA,CAAA,KAAK,MAAM,QAAQ,CAAA;AAAA,IAC9D,CAAA;AAAA,EACF;AAAA,EAEA,KAAK,KAAA,EAAgB;AAInB,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,UAAA,CAAW,KAAA,EAAM;AACxC,IAAA,KAAA,MAAW,YAAY,SAAA,EAAW;AAChC,MAAA,IAAI;AACF,QAAA,QAAA,CAAS,KAAK,CAAA;AAAA,MAChB,SAAS,KAAA,EAAO;AACd,QAAA,gBAAA,CAAiB,KAAK,CAAA;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,YAAA,GAAkC;AAChC,IAAA,OAAO,IAAI,cAAA,CAAkB,CAAA,QAAA,KAAY,IAAA,CAAK,SAAA,CAAU,QAAQ,CAAC,CAAA;AAAA,EACnE;AACF,CAAA;AAMO,IAAM,mBAAA,GAAN,cAAqC,WAAA,CAAe;AAAA,EACjD,MAAA;AAAA,EAER,YAAY,YAAA,EAAiB;AAC3B,IAAA,KAAA,EAAM;AACN,IAAA,IAAA,CAAK,MAAA,GAAS,YAAA;AAAA,EAChB;AAAA,EAEA,QAAA,GAAc;AACZ,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AAAA,EAES,KAAK,KAAA,EAAgB;AAC5B,IAAA,IAAA,CAAK,MAAA,GAAS,KAAA;AACd,IAAA,KAAA,CAAM,KAAK,KAAK,CAAA;AAAA,EAClB;AAAA,EAES,UAAU,QAAA,EAAoC;AACrD,IAAA,IAAI;AACF,MAAA,QAAA,CAAS,KAAK,MAAM,CAAA;AAAA,IACtB,SAAS,KAAA,EAAO;AACd,MAAA,gBAAA,CAAiB,KAAK,CAAA;AAAA,IACxB;AACA,IAAA,OAAO,KAAA,CAAM,UAAU,QAAQ,CAAA;AAAA,EACjC;AAAA,EAES,YAAA,GAAkC;AACzC,IAAA,OAAO,IAAI,cAAA,CAAkB,CAAA,QAAA,KAAY,IAAA,CAAK,SAAA,CAAU,QAAQ,CAAC,CAAA;AAAA,EACnE;AACF,CAAA;AAKO,IAAM,iBAAN,MAAwB;AAAA,EAC7B,YAAoB,YAAA,EAAsD;AAAtD,IAAA,IAAA,CAAA,YAAA,GAAA,YAAA;AAAA,EAAuD;AAAA,EAAvD,YAAA;AAAA,EAEpB,UAAU,QAAA,EAAoC;AAC5C,IAAA,OAAO,IAAA,CAAK,aAAa,QAAQ,CAAA;AAAA,EACnC;AAAA;AAAA,EAGA,OAAA,GAAyB;AACvB,IAAA,IAAI,MAAA;AACJ,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,SAAA,CAAU,CAAA,CAAA,KAAK;AAAE,MAAA,MAAA,GAAS,CAAA;AAAA,IAAG,CAAC,CAAA;AACjD,IAAA,KAAA,EAAM;AACN,IAAA,OAAO,MAAA;AAAA,EACT;AACF,CAAA;;;ACpEA,IAAM,iBAAA,GAAiE;AAAA,EACrE,MAAA,EAAW,EAAE,OAAA,EAAS,wBAAA,EAAyB;AAAA,EAC/C,SAAA,EAAW,EAAE,OAAA,EAAS,2BAAA,EAA4B;AAAA,EAClD,MAAA,EAAW,EAAE,OAAA,EAAS,2CAAA,EAA4C;AAAA,EAClE,MAAA,EAAW,EAAE,OAAA,EAAS,wBAAA,EAAyB;AAAA,EAC/C,MAAA,EAAW,EAAE,OAAA,EAAS,EAAA;AACxB,CAAA;AAEO,IAAM,eAAN,MAAmB;AAAA,EAChB,IAAA;AAAA,EACA,OAAA,GAAU,IAAI,mBAAA,CAAiC,EAAE,YAAA,EAAc,GAAG,gBAAA,EAAkB,CAAA,EAAG,WAAA,EAAa,CAAA,EAAG,CAAA;AAAA,EACvG,MAAA,GAAiC,IAAA;AAAA,EACjC,WAA4B,EAAC;AAAA,EAErC,YAAY,MAAA,EAAsB;AAAE,IAAA,IAAA,CAAK,IAAA,GAAO,MAAA;AAAA,EAAQ;AAAA,EAExD,UAAU,KAAA,EAAoC;AAAE,IAAA,IAAA,CAAK,OAAO,EAAE,GAAG,IAAA,CAAK,IAAA,EAAM,GAAG,KAAA,EAAM;AAAA,EAAG;AAAA,EACxF,SAAA,GAAyB;AAAE,IAAA,OAAO,IAAA,CAAK,QAAQ,QAAA,EAAS;AAAA,EAAG;AAAA,EAC3D,MAAA,GAAS;AAAE,IAAA,OAAO,IAAA,CAAK,QAAQ,YAAA,EAAa;AAAA,EAAG;AAAA,EAC/C,UAAA,GAA8B;AAAE,IAAA,OAAO,CAAC,GAAG,IAAA,CAAK,QAAQ,CAAA;AAAA,EAAG;AAAA,EAC3D,YAAA,GAAqB;AAAE,IAAA,IAAA,CAAK,WAAW,EAAC;AAAA,EAAG;AAAA,EAE3C,KAAA,GAAc;AAAE,IAAA,IAAA,CAAK,QAAQ,KAAA,EAAM;AAAG,IAAA,IAAA,CAAK,MAAA,GAAS,IAAA;AAAA,EAAM;AAAA;AAAA,EAG1D,eAAe,IAAA,EAAsB;AAAE,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,IAAA,CAAK,MAAA,GAAS,CAAC,CAAA;AAAA,EAAG;AAAA,EAE1E,MAAM,QAAA,CAAS,MAAA,EAAgB,IAAA,GAA4B,EAAC,EAAoB;AAC9E,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,CAAC,EAAE,IAAA,EAAM,QAAQ,OAAA,EAAS,MAAA,EAAQ,CAAA,EAAG,IAAI,CAAA;AAAA,EAC5D;AAAA,EAEA,MAAM,IAAA,CAAK,QAAA,EAA2B,IAAA,GAA4B,EAAC,EAAoB;AACrF,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,eAAA,EAAgB;AAClC,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,MAAA,GAChB,eAAA,CAAgB,IAAA,CAAK,MAAA,EAAQ,IAAA,CAAK,MAAA,CAAO,MAAM,CAAA,GAC/C,IAAA,CAAK,MAAA,CAAO,MAAA;AAEhB,IAAA,MAAM,cAA+B,EAAC;AACtC,IAAA,IAAI,IAAA,CAAK,IAAA,CAAK,YAAA,EAAc,WAAA,CAAY,IAAA,CAAK,EAAE,IAAA,EAAM,QAAA,EAAU,OAAA,EAAS,IAAA,CAAK,IAAA,CAAK,YAAA,EAAc,CAAA;AAChG,IAAA,WAAA,CAAY,IAAA,CAAK,GAAG,IAAA,CAAK,QAAA,EAAU,GAAG,QAAQ,CAAA;AAE9C,IAAA,MAAM,SAAS,IAAA,CAAK,MAAA,KAAW,KAAA,GAC3B,MAAM,KAAK,WAAA,CAAY,WAAA,EAAa,IAAA,EAAM,MAAM,IAChD,MAAM,IAAA,CAAK,UAAA,CAAW,WAAA,EAAa,MAAM,MAAM,CAAA;AAGnD,IAAA,KAAA,MAAW,CAAA,IAAK,QAAA,EAAU,IAAA,CAAK,QAAA,CAAS,KAAK,CAAC,CAAA;AAC9C,IAAA,IAAA,CAAK,SAAS,IAAA,CAAK,EAAE,MAAM,WAAA,EAAa,OAAA,EAAS,QAAQ,CAAA;AAEzD,IAAA,IAAA,CAAK,aAAa,MAAM,CAAA;AACxB,IAAA,OAAO,MAAA;AAAA,EACT;AAAA,EAEA,MAAc,UAAA,CAAW,QAAA,EAA2B,IAAA,EAA2B,MAAA,EAAsC;AACnH,IAAA,MAAM,EAAE,KAAK,OAAA,EAAS,IAAA,KAAS,IAAA,CAAK,aAAA,CAAc,QAAA,EAAU,IAAA,EAAM,KAAK,CAAA;AACvE,IAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,GAAA,EAAK,EAAE,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA,EAAG,QAAQ,CAAA;AAC5F,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,KAAA,CAAM,CAAA,cAAA,EAAiB,GAAA,CAAI,MAAM,CAAA,CAAA,EAAI,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,CAAA;AAC9E,IAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,IAAA,OAAO,IAAA,CAAK,gBAAgB,IAAI,CAAA;AAAA,EAClC;AAAA,EAEA,MAAc,WAAA,CAAY,QAAA,EAA2B,IAAA,EAA2B,MAAA,EAAsC;AACpH,IAAA,MAAM,EAAE,KAAK,OAAA,EAAS,IAAA,KAAS,IAAA,CAAK,aAAA,CAAc,QAAA,EAAU,IAAA,EAAM,IAAI,CAAA;AACtE,IAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,GAAA,EAAK,EAAE,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA,EAAG,QAAQ,CAAA;AAC5F,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,KAAA,CAAM,CAAA,cAAA,EAAiB,GAAA,CAAI,MAAM,CAAA,CAAA,EAAI,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA,CAAE,CAAA;AAE9E,IAAA,MAAM,MAAA,GAAS,GAAA,CAAI,IAAA,CAAM,SAAA,EAAU;AACnC,IAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAChC,IAAA,IAAI,IAAA,GAAO,EAAA;AACX,IAAA,IAAI,GAAA,GAAM,EAAA;AAEV,IAAA,OAAO,IAAA,EAAM;AACX,MAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAM,GAAI,MAAM,OAAO,IAAA,EAAK;AAC1C,MAAA,IAAI,IAAA,EAAM;AACV,MAAA,GAAA,IAAO,QAAQ,MAAA,CAAO,KAAA,EAAO,EAAE,MAAA,EAAQ,MAAM,CAAA;AAC7C,MAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,KAAA,CAAM,IAAI,CAAA;AAC5B,MAAA,GAAA,GAAM,KAAA,CAAM,KAAI,IAAK,EAAA;AACrB,MAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,QAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,gBAAA,CAAiB,IAAA,CAAK,MAAM,CAAA;AAC/C,QAAA,IAAI,KAAA,EAAO;AAAE,UAAA,IAAA,IAAQ,KAAA;AAAO,UAAA,IAAA,CAAK,UAAU,KAAK,CAAA;AAAA,QAAG;AAAA,MACrD;AAAA,IACF;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEQ,aAAA,CAAc,QAAA,EAA2B,IAAA,EAA2B,MAAA,EAAkF;AAC5J,IAAA,MAAM,EAAE,QAAA,EAAU,MAAA,EAAQ,OAAO,SAAA,EAAW,WAAA,KAAgB,IAAA,CAAK,IAAA;AACjE,IAAA,MAAM,UAAU,IAAA,CAAK,IAAA,CAAK,OAAA,IAAW,iBAAA,CAAkB,QAAQ,CAAA,CAAE,OAAA;AACjE,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,SAAA,IAAa,SAAA,IAAa,IAAA;AAC1C,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,WAAA,IAAe,WAAA,IAAe,GAAA;AAEhD,IAAA,QAAQ,QAAA;AAAU,MAChB,KAAK,QAAA;AAAA,MACL,KAAK,QAAA;AACH,QAAA,OAAO;AAAA,UACL,GAAA,EAAK,GAAG,OAAO,CAAA,oBAAA,CAAA;AAAA,UACf,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAoB,GAAI,MAAA,GAAS,EAAE,aAAA,EAAe,CAAA,OAAA,EAAU,MAAM,CAAA,CAAA,EAAG,GAAI,EAAC,EAAG;AAAA,UACxG,IAAA,EAAM,EAAE,KAAA,EAAO,QAAA,EAAU,QAAQ,UAAA,EAAY,EAAA,EAAI,aAAa,IAAA;AAAK,SACrE;AAAA,MACF,KAAK,WAAA;AACH,QAAA,OAAO;AAAA,UACL,GAAA,EAAK,GAAG,OAAO,CAAA,YAAA,CAAA;AAAA,UACf,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAoB,aAAa,MAAA,IAAU,EAAA,EAAI,qBAAqB,YAAA,EAAa;AAAA,UAC5G,IAAA,EAAM;AAAA,YACJ,KAAA;AAAA,YAAO,MAAA;AAAA,YAAQ,UAAA,EAAY,EAAA;AAAA,YAAI,WAAA,EAAa,IAAA;AAAA,YAC5C,QAAQ,QAAA,CAAS,IAAA,CAAK,OAAK,CAAA,CAAE,IAAA,KAAS,QAAQ,CAAA,EAAG,OAAA;AAAA,YACjD,UAAU,QAAA,CAAS,MAAA,CAAO,CAAA,CAAA,KAAK,CAAA,CAAE,SAAS,QAAQ;AAAA;AACpD,SACF;AAAA,MACF,KAAK,QAAA,EAAU;AACb,QAAA,MAAM,QAAA,GAAW,QAAA,CAAS,MAAA,CAAO,CAAA,CAAA,KAAK,CAAA,CAAE,SAAS,QAAQ,CAAA,CAAE,GAAA,CAAI,CAAA,CAAA,MAAM,EAAE,IAAA,EAAM,EAAE,IAAA,KAAS,WAAA,GAAc,OAAA,GAAU,MAAA,EAAQ,KAAA,EAAO,CAAC,EAAE,IAAA,EAAM,CAAA,CAAE,OAAA,EAAS,CAAA,EAAE,CAAE,CAAA;AACvJ,QAAA,MAAM,IAAA,GAAO,SAAS,+BAAA,GAAkC,iBAAA;AACxD,QAAA,OAAO;AAAA,UACL,GAAA,EAAK,CAAA,EAAG,OAAO,CAAA,eAAA,EAAkB,KAAK,CAAA,CAAA,EAAI,IAAI,CAAA,EAAG,MAAA,GAAS,CAAA,KAAA,EAAQ,MAAM,CAAA,CAAA,GAAK,EAAE,CAAA,CAAA;AAAA,UAC/E,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,UAC9C,IAAA,EAAM,EAAE,QAAA,EAAU,gBAAA,EAAkB,EAAE,eAAA,EAAiB,EAAA,EAAI,WAAA,EAAa,IAAA,EAAK;AAAE,SACjF;AAAA,MACF;AAAA,MACA,KAAK,QAAA;AACH,QAAA,OAAO;AAAA,UACL,GAAA,EAAK,GAAG,OAAO,CAAA,SAAA,CAAA;AAAA,UACf,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,UAC9C,IAAA,EAAM,EAAE,KAAA,EAAO,QAAA,EAAU,MAAA,EAAQ,OAAA,EAAS,EAAE,WAAA,EAAa,EAAA,EAAI,WAAA,EAAa,IAAA,EAAK;AAAE,SACnF;AAAA;AACJ,EACF;AAAA,EAEQ,iBAAiB,IAAA,EAAsB;AAC7C,IAAA,IAAI,CAAC,IAAA,CAAK,UAAA,CAAW,QAAQ,CAAA,IAAK,CAAC,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,EAAG,OAAO,EAAA;AAChE,IAAA,MAAM,GAAA,GAAM,KAAK,UAAA,CAAW,QAAQ,IAAI,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA,GAAI,IAAA;AACxD,IAAA,IAAI,GAAA,KAAQ,UAAU,OAAO,EAAA;AAC7B,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAE3B,MAAA,MAAM,QAAA,GAAW,IAAA,EAAM,OAAA,GAAU,CAAC,CAAA;AAClC,MAAA,IAAI,QAAA,EAAU,KAAA,EAAO,OAAA,EAAS,OAAO,SAAS,KAAA,CAAM,OAAA;AAEpD,MAAA,IAAI,MAAM,IAAA,KAAS,qBAAA,EAAuB,OAAO,IAAA,EAAM,OAAO,IAAA,IAAQ,EAAA;AAEtE,MAAA,MAAM,SAAS,IAAA,EAAM,UAAA,GAAa,CAAC,CAAA,EAAG,OAAA,EAAS,QAAQ,CAAC,CAAA;AACxD,MAAA,IAAI,MAAA,EAAQ,IAAA,EAAM,OAAO,MAAA,CAAO,IAAA;AAEhC,MAAA,IAAI,IAAA,EAAM,OAAA,EAAS,OAAA,EAAS,OAAO,KAAK,OAAA,CAAQ,OAAA;AAAA,IAClD,CAAA,CAAA,MAAQ;AAAA,IAAC;AACT,IAAA,OAAO,EAAA;AAAA,EACT;AAAA,EAEQ,gBAAgB,IAAA,EAAuB;AAC7C,IAAA,MAAM,CAAA,GAAI,IAAA;AAEV,IAAA,MAAM,EAAA,GAAM,CAAA,EAAG,OAAA,GAAgD,CAAC,GAAG,OAAA,EAAS,OAAA;AAC5E,IAAA,IAAI,IAAI,OAAO,EAAA;AAEf,IAAA,MAAM,EAAA,GAAM,CAAA,EAAG,OAAA,GAAmC,CAAC,CAAA,EAAG,IAAA;AACtD,IAAA,IAAI,IAAI,OAAO,EAAA;AAEf,IAAA,MAAM,EAAA,GAAO,GAAG,UAAA,GAA+D,CAAC,GAAG,OAAA,EAAS,KAAA,GAAS,CAAC,CAAA,EAAG,IAAA;AACzG,IAAA,IAAI,IAAI,OAAO,EAAA;AAEf,IAAA,MAAM,EAAA,GAAM,GAAG,OAAA,EAA8B,OAAA;AAC7C,IAAA,IAAI,IAAI,OAAO,EAAA;AACf,IAAA,OAAO,EAAA;AAAA,EACT;AACF;AAEA,SAAS,mBAAmB,OAAA,EAAqC;AAC/D,EAAA,MAAM,IAAA,GAAO,IAAI,eAAA,EAAgB;AACjC,EAAA,KAAA,MAAW,CAAA,IAAK,OAAA,EAAS,CAAA,CAAE,gBAAA,CAAiB,OAAA,EAAS,MAAM,IAAA,CAAK,KAAA,EAAM,EAAG,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AACvF,EAAA,OAAO,IAAA,CAAK,MAAA;AACd;;;AC/KO,IAAM,mBAAN,MAAuB;AAAA,EACpB,MAAA,uBAAa,GAAA,EAAgC;AAAA,EAErD,SAAS,IAAA,EAAgC;AACvC,IAAA,IAAI,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,IAAI,CAAA,EAAG,MAAM,IAAI,KAAA,CAAM,CAAA,MAAA,EAAS,IAAA,CAAK,IAAI,CAAA,8CAAA,CAAgD,CAAA;AAClH,IAAA,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,IAAA,EAAM,IAAI,CAAA;AAAA,EACjC;AAAA,EAEA,OAAO,IAAA,EAAgC;AAAE,IAAA,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAA,CAAK,IAAA,EAAM,IAAI,CAAA;AAAA,EAAG;AAAA,EAC3E,WAAW,IAAA,EAAoB;AAAE,IAAA,IAAA,CAAK,MAAA,CAAO,OAAO,IAAI,CAAA;AAAA,EAAG;AAAA,EAC3D,IAAI,IAAA,EAAuB;AAAE,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAI,CAAA;AAAA,EAAG;AAAA,EAC3D,IAAA,GAAiB;AAAE,IAAA,OAAO,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,MAAA,CAAO,MAAM,CAAA;AAAA,EAAG;AAAA,EAC1D,IAAI,IAAA,EAA8C;AAAE,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,IAAI,CAAA;AAAA,EAAG;AAAA,EAClF,KAAA,GAAc;AAAE,IAAA,IAAA,CAAK,OAAO,KAAA,EAAM;AAAA,EAAG;AAAA,EAErC,MAAM,SAAS,IAAA,EAA6C;AAC1D,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,KAAK,IAAI,CAAA;AACtC,IAAA,IAAI,CAAC,IAAA,EAAM,OAAO,EAAE,MAAM,IAAA,CAAK,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAM,OAAO,CAAA,cAAA,EAAiB,IAAA,CAAK,IAAI,CAAA,CAAA,EAAI,YAAY,CAAA,EAAE;AAEtG,IAAA,MAAM,IAAA,GAAO,OAAO,IAAA,CAAK,SAAA,KAAc,QAAA,GAAW,KAAK,KAAA,CAAM,IAAA,CAAK,SAAS,CAAA,GAAI,IAAA,CAAK,SAAA;AACpF,IAAA,MAAM,EAAA,GAAK,KAAK,GAAA,EAAI;AACpB,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,OAAA,CAAQ,IAA+B,CAAA;AACjE,MAAA,OAAO,EAAE,MAAM,IAAA,CAAK,IAAA,EAAM,QAAQ,UAAA,EAAY,IAAA,CAAK,GAAA,EAAI,GAAI,EAAA,EAAG;AAAA,IAChE,SAAS,CAAA,EAAG;AACV,MAAA,OAAO,EAAE,IAAA,EAAM,IAAA,CAAK,IAAA,EAAM,QAAQ,IAAA,EAAM,KAAA,EAAO,MAAA,CAAO,CAAC,CAAA,EAAG,UAAA,EAAY,IAAA,CAAK,GAAA,KAAQ,EAAA,EAAG;AAAA,IACxF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,YAAY,KAAA,EAAkD;AAClE,IAAA,OAAO,OAAA,CAAQ,IAAI,KAAA,CAAM,GAAA,CAAI,OAAK,IAAA,CAAK,QAAA,CAAS,CAAC,CAAC,CAAC,CAAA;AAAA,EACrD;AAAA;AAAA,EAGA,cAAA,GAA2B;AACzB,IAAA,OAAO,KAAA,CAAM,KAAK,IAAA,CAAK,MAAA,CAAO,QAAQ,CAAA,CAAE,IAAI,CAAA,CAAA,MAAM;AAAA,MAChD,IAAA,EAAM,UAAA;AAAA,MACN,QAAA,EAAU;AAAA,QACR,MAAM,CAAA,CAAE,IAAA;AAAA,QACR,aAAa,CAAA,CAAE,WAAA;AAAA,QACf,UAAA,EAAY;AAAA,UACV,IAAA,EAAM,QAAA;AAAA,UACN,YAAY,CAAA,CAAE,UAAA;AAAA,UACd,QAAA,EAAU,CAAA,CAAE,QAAA,IAAY;AAAC;AAC3B;AACF,KACF,CAAE,CAAA;AAAA,EACJ;AAAA;AAAA,EAGA,iBAAA,GAA8B;AAC5B,IAAA,OAAO,KAAA,CAAM,KAAK,IAAA,CAAK,MAAA,CAAO,QAAQ,CAAA,CAAE,IAAI,CAAA,CAAA,MAAM;AAAA,MAChD,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,aAAa,CAAA,CAAE,WAAA;AAAA,MACf,YAAA,EAAc;AAAA,QACZ,IAAA,EAAM,QAAA;AAAA,QACN,YAAY,CAAA,CAAE,UAAA;AAAA,QACd,QAAA,EAAU,CAAA,CAAE,QAAA,IAAY;AAAC;AAC3B,KACF,CAAE,CAAA;AAAA,EACJ;AAAA;AAAA,EAGA,cAAA,GAA2B;AACzB,IAAA,OAAO,CAAC;AAAA,MACN,qBAAA,EAAuB,MAAM,IAAA,CAAK,IAAA,CAAK,OAAO,MAAA,EAAQ,CAAA,CAAE,GAAA,CAAI,CAAA,CAAA,MAAM;AAAA,QAChE,MAAM,CAAA,CAAE,IAAA;AAAA,QACR,aAAa,CAAA,CAAE,WAAA;AAAA,QACf,UAAA,EAAY,EAAE,IAAA,EAAM,QAAA,EAAU,UAAA,EAAY,CAAA,CAAE,UAAA,EAAY,QAAA,EAAU,CAAA,CAAE,QAAA,IAAY,EAAC;AAAE,OACrF,CAAE;AAAA,KACH,CAAA;AAAA,EACH;AACF","file":"index.js","sourcesContent":["export type Unsubscribe = () => void;\nexport type Listener<T> = (value: T) => void;\n\n/**\n * Reports a subscriber that threw during emission, without aborting delivery\n * to the remaining subscribers. Defaults to console.error; override to route\n * to your own logger (e.g. in a banking app where dropped events matter).\n */\nlet _onListenerError: (error: unknown) => void = (error) => {\n // eslint-disable-next-line no-console\n console.error('[joopjs] a subject subscriber threw during emission:', error);\n};\n\n/** Override how subscriber errors are reported during emission. */\nexport function setSubjectErrorHandler(handler: (error: unknown) => void): void {\n _onListenerError = handler;\n}\n\n/**\n * Lightweight pub/sub subject — emits to all current subscribers.\n * Replaces RxJS Subject without pulling in the full RxJS dependency.\n */\nexport class JoopSubject<T> {\n private _listeners: Listener<T>[] = [];\n\n subscribe(listener: Listener<T>): Unsubscribe {\n this._listeners.push(listener);\n return () => {\n this._listeners = this._listeners.filter(l => l !== listener);\n };\n }\n\n next(value: T): void {\n // Snapshot so a subscribe()/unsubscribe() triggered by a listener can't\n // disturb this emission, and isolate each listener so one that throws\n // doesn't deprive later subscribers of the value.\n const listeners = this._listeners.slice();\n for (const listener of listeners) {\n try {\n listener(value);\n } catch (error) {\n _onListenerError(error);\n }\n }\n }\n\n asObservable(): JoopObservable<T> {\n return new JoopObservable<T>(listener => this.subscribe(listener));\n }\n}\n\n/**\n * Stateful subject — replays the current value to any new subscriber immediately.\n * Replaces RxJS BehaviorSubject.\n */\nexport class JoopBehaviorSubject<T> extends JoopSubject<T> {\n private _value: T;\n\n constructor(initialValue: T) {\n super();\n this._value = initialValue;\n }\n\n getValue(): T {\n return this._value;\n }\n\n override next(value: T): void {\n this._value = value;\n super.next(value);\n }\n\n override subscribe(listener: Listener<T>): Unsubscribe {\n try {\n listener(this._value);\n } catch (error) {\n _onListenerError(error);\n }\n return super.subscribe(listener);\n }\n\n override asObservable(): JoopObservable<T> {\n return new JoopObservable<T>(listener => this.subscribe(listener));\n }\n}\n\n/**\n * Read-only observable handle returned from asObservable().\n */\nexport class JoopObservable<T> {\n constructor(private _subscribeFn: (listener: Listener<T>) => Unsubscribe) {}\n\n subscribe(listener: Listener<T>): Unsubscribe {\n return this._subscribeFn(listener);\n }\n\n /** Returns the current value without subscribing (only meaningful for BehaviorSubject-backed observables). */\n getOnce(): T | undefined {\n let result: T | undefined;\n const unsub = this.subscribe(v => { result = v; });\n unsub();\n return result;\n }\n}\n","import { JoopBehaviorSubject } from '../events';\n\nexport type JoopAiProvider = 'openai' | 'anthropic' | 'gemini' | 'ollama' | 'custom';\n\nexport interface JoopAiMessage {\n role: 'system' | 'user' | 'assistant';\n content: string;\n}\n\nexport interface JoopAiConfig {\n provider: JoopAiProvider;\n apiKey?: string;\n baseUrl?: string;\n model: string;\n maxTokens?: number;\n temperature?: number;\n systemPrompt?: string;\n timeout?: number;\n}\n\nexport interface JoopAiStreamOptions {\n stream?: boolean;\n onChunk?: (token: string) => void;\n onComplete?: (full: string) => void;\n signal?: AbortSignal;\n maxTokens?: number;\n temperature?: number;\n}\n\nexport interface JoopAiUsage {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n}\n\nconst PROVIDER_DEFAULTS: Record<JoopAiProvider, { baseUrl: string }> = {\n openai: { baseUrl: 'https://api.openai.com' },\n anthropic: { baseUrl: 'https://api.anthropic.com' },\n gemini: { baseUrl: 'https://generativelanguage.googleapis.com' },\n ollama: { baseUrl: 'http://localhost:11434' },\n custom: { baseUrl: '' },\n};\n\nexport class JoopAiClient {\n private _cfg: JoopAiConfig;\n private _usage$ = new JoopBehaviorSubject<JoopAiUsage>({ promptTokens: 0, completionTokens: 0, totalTokens: 0 });\n private _abort: AbortController | null = null;\n private _history: JoopAiMessage[] = [];\n\n constructor(config: JoopAiConfig) { this._cfg = config; }\n\n configure(patch: Partial<JoopAiConfig>): void { this._cfg = { ...this._cfg, ...patch }; }\n lastUsage(): JoopAiUsage { return this._usage$.getValue(); }\n usage$() { return this._usage$.asObservable(); }\n getHistory(): JoopAiMessage[] { return [...this._history]; }\n clearHistory(): void { this._history = []; }\n\n abort(): void { this._abort?.abort(); this._abort = null; }\n\n /** Approximate token count (4 chars ≈ 1 token) */\n estimateTokens(text: string): number { return Math.ceil(text.length / 4); }\n\n async complete(prompt: string, opts: JoopAiStreamOptions = {}): Promise<string> {\n return this.chat([{ role: 'user', content: prompt }], opts);\n }\n\n async chat(messages: JoopAiMessage[], opts: JoopAiStreamOptions = {}): Promise<string> {\n this._abort = new AbortController();\n const signal = opts.signal\n ? _combineSignals(opts.signal, this._abort.signal)\n : this._abort.signal;\n\n const allMessages: JoopAiMessage[] = [];\n if (this._cfg.systemPrompt) allMessages.push({ role: 'system', content: this._cfg.systemPrompt });\n allMessages.push(...this._history, ...messages);\n\n const result = opts.stream !== false\n ? await this._streamChat(allMessages, opts, signal)\n : await this._fetchChat(allMessages, opts, signal);\n\n // Append to history\n for (const m of messages) this._history.push(m);\n this._history.push({ role: 'assistant', content: result });\n\n opts.onComplete?.(result);\n return result;\n }\n\n private async _fetchChat(messages: JoopAiMessage[], opts: JoopAiStreamOptions, signal: AbortSignal): Promise<string> {\n const { url, headers, body } = this._buildRequest(messages, opts, false);\n const res = await fetch(url, { method: 'POST', headers, body: JSON.stringify(body), signal });\n if (!res.ok) throw new Error(`JoopAiClient: ${res.status} ${await res.text()}`);\n const json = await res.json();\n return this._extractContent(json);\n }\n\n private async _streamChat(messages: JoopAiMessage[], opts: JoopAiStreamOptions, signal: AbortSignal): Promise<string> {\n const { url, headers, body } = this._buildRequest(messages, opts, true);\n const res = await fetch(url, { method: 'POST', headers, body: JSON.stringify(body), signal });\n if (!res.ok) throw new Error(`JoopAiClient: ${res.status} ${await res.text()}`);\n\n const reader = res.body!.getReader();\n const decoder = new TextDecoder();\n let full = '';\n let buf = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n const lines = buf.split('\\n');\n buf = lines.pop() ?? '';\n for (const line of lines) {\n const token = this._parseStreamLine(line.trim());\n if (token) { full += token; opts.onChunk?.(token); }\n }\n }\n return full;\n }\n\n private _buildRequest(messages: JoopAiMessage[], opts: JoopAiStreamOptions, stream: boolean): { url: string; headers: Record<string, string>; body: unknown } {\n const { provider, apiKey, model, maxTokens, temperature } = this._cfg;\n const baseUrl = this._cfg.baseUrl ?? PROVIDER_DEFAULTS[provider].baseUrl;\n const mt = opts.maxTokens ?? maxTokens ?? 1024;\n const temp = opts.temperature ?? temperature ?? 0.7;\n\n switch (provider) {\n case 'openai':\n case 'custom':\n return {\n url: `${baseUrl}/v1/chat/completions`,\n headers: { 'Content-Type': 'application/json', ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}) },\n body: { model, messages, stream, max_tokens: mt, temperature: temp },\n };\n case 'anthropic':\n return {\n url: `${baseUrl}/v1/messages`,\n headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey ?? '', 'anthropic-version': '2023-06-01' },\n body: {\n model, stream, max_tokens: mt, temperature: temp,\n system: messages.find(m => m.role === 'system')?.content,\n messages: messages.filter(m => m.role !== 'system'),\n },\n };\n case 'gemini': {\n const contents = messages.filter(m => m.role !== 'system').map(m => ({ role: m.role === 'assistant' ? 'model' : 'user', parts: [{ text: m.content }] }));\n const path = stream ? 'streamGenerateContent?alt=sse' : 'generateContent';\n return {\n url: `${baseUrl}/v1beta/models/${model}:${path}${apiKey ? `&key=${apiKey}` : ''}`,\n headers: { 'Content-Type': 'application/json' },\n body: { contents, generationConfig: { maxOutputTokens: mt, temperature: temp } },\n };\n }\n case 'ollama':\n return {\n url: `${baseUrl}/api/chat`,\n headers: { 'Content-Type': 'application/json' },\n body: { model, messages, stream, options: { num_predict: mt, temperature: temp } },\n };\n }\n }\n\n private _parseStreamLine(line: string): string {\n if (!line.startsWith('data: ') && !line.startsWith('{')) return '';\n const raw = line.startsWith('data: ') ? line.slice(6) : line;\n if (raw === '[DONE]') return '';\n try {\n const json = JSON.parse(raw);\n // OpenAI\n const oaChoice = json?.choices?.[0];\n if (oaChoice?.delta?.content) return oaChoice.delta.content;\n // Anthropic\n if (json?.type === 'content_block_delta') return json?.delta?.text ?? '';\n // Gemini\n const gmPart = json?.candidates?.[0]?.content?.parts?.[0];\n if (gmPart?.text) return gmPart.text;\n // Ollama\n if (json?.message?.content) return json.message.content;\n } catch {}\n return '';\n }\n\n private _extractContent(json: unknown): string {\n const j = json as Record<string, unknown>;\n // OpenAI\n const oa = (j?.choices as Array<{message:{content:string}}>)?.[0]?.message?.content;\n if (oa) return oa;\n // Anthropic\n const an = (j?.content as Array<{text:string}>)?.[0]?.text;\n if (an) return an;\n // Gemini\n const gm = ((j?.candidates as Array<{content:{parts:Array<{text:string}>}}>)?.[0]?.content?.parts)?.[0]?.text;\n if (gm) return gm;\n // Ollama\n const ol = (j?.message as {content:string})?.content;\n if (ol) return ol;\n return '';\n }\n}\n\nfunction _combineSignals(...signals: AbortSignal[]): AbortSignal {\n const ctrl = new AbortController();\n for (const s of signals) s.addEventListener('abort', () => ctrl.abort(), { once: true });\n return ctrl.signal;\n}\n","export interface JoopToolParameter {\n type: 'string' | 'number' | 'boolean' | 'object' | 'array';\n description?: string;\n enum?: unknown[];\n items?: JoopToolParameter;\n properties?: Record<string, JoopToolParameter>;\n required?: string[];\n}\n\nexport interface JoopToolDefinition {\n name: string;\n description: string;\n parameters: Record<string, JoopToolParameter>;\n required?: string[];\n handler: (args: Record<string, unknown>) => Promise<unknown>;\n}\n\nexport interface JoopToolCall {\n name: string;\n arguments: string | Record<string, unknown>;\n}\n\nexport interface JoopToolResult {\n name: string;\n result: unknown;\n error?: string;\n durationMs: number;\n}\n\nexport class JoopToolRegistry {\n private _tools = new Map<string, JoopToolDefinition>();\n\n register(tool: JoopToolDefinition): void {\n if (this._tools.has(tool.name)) throw new Error(`Tool '${tool.name}' already registered. Use update() to replace.`);\n this._tools.set(tool.name, tool);\n }\n\n update(tool: JoopToolDefinition): void { this._tools.set(tool.name, tool); }\n unregister(name: string): void { this._tools.delete(name); }\n has(name: string): boolean { return this._tools.has(name); }\n list(): string[] { return Array.from(this._tools.keys()); }\n get(name: string): JoopToolDefinition | undefined { return this._tools.get(name); }\n clear(): void { this._tools.clear(); }\n\n async dispatch(call: JoopToolCall): Promise<JoopToolResult> {\n const tool = this._tools.get(call.name);\n if (!tool) return { name: call.name, result: null, error: `Unknown tool: ${call.name}`, durationMs: 0 };\n\n const args = typeof call.arguments === 'string' ? JSON.parse(call.arguments) : call.arguments;\n const t0 = Date.now();\n try {\n const result = await tool.handler(args as Record<string, unknown>);\n return { name: call.name, result, durationMs: Date.now() - t0 };\n } catch (e) {\n return { name: call.name, result: null, error: String(e), durationMs: Date.now() - t0 };\n }\n }\n\n /** Dispatch multiple tool calls in parallel */\n async dispatchAll(calls: JoopToolCall[]): Promise<JoopToolResult[]> {\n return Promise.all(calls.map(c => this.dispatch(c)));\n }\n\n /** Format for OpenAI function-calling */\n toOpenAiFormat(): object[] {\n return Array.from(this._tools.values()).map(t => ({\n type: 'function',\n function: {\n name: t.name,\n description: t.description,\n parameters: {\n type: 'object',\n properties: t.parameters,\n required: t.required ?? [],\n },\n },\n }));\n }\n\n /** Format for Anthropic tool-use */\n toAnthropicFormat(): object[] {\n return Array.from(this._tools.values()).map(t => ({\n name: t.name,\n description: t.description,\n input_schema: {\n type: 'object',\n properties: t.parameters,\n required: t.required ?? [],\n },\n }));\n }\n\n /** Format for Google Gemini function declarations */\n toGeminiFormat(): object[] {\n return [{\n function_declarations: Array.from(this._tools.values()).map(t => ({\n name: t.name,\n description: t.description,\n parameters: { type: 'object', properties: t.parameters, required: t.required ?? [] },\n })),\n }];\n }\n}\n"]}
package/dist/ai/index.mjs CHANGED
@@ -1,4 +1,7 @@
1
1
  // src/events/index.ts
2
+ var _onListenerError = (error) => {
3
+ console.error("[joopjs] a subject subscriber threw during emission:", error);
4
+ };
2
5
  var JoopSubject = class {
3
6
  _listeners = [];
4
7
  subscribe(listener) {
@@ -8,8 +11,13 @@ var JoopSubject = class {
8
11
  };
9
12
  }
10
13
  next(value) {
11
- for (const listener of this._listeners) {
12
- listener(value);
14
+ const listeners = this._listeners.slice();
15
+ for (const listener of listeners) {
16
+ try {
17
+ listener(value);
18
+ } catch (error) {
19
+ _onListenerError(error);
20
+ }
13
21
  }
14
22
  }
15
23
  asObservable() {
@@ -30,7 +38,11 @@ var JoopBehaviorSubject = class extends JoopSubject {
30
38
  super.next(value);
31
39
  }
32
40
  subscribe(listener) {
33
- listener(this._value);
41
+ try {
42
+ listener(this._value);
43
+ } catch (error) {
44
+ _onListenerError(error);
45
+ }
34
46
  return super.subscribe(listener);
35
47
  }
36
48
  asObservable() {