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
@@ -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/cache/cache.service.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 interface JoopCacheOptions {\n ttlMs?: number;\n maxSize?: number;\n}\n\ninterface CacheEntry<T> {\n value: T;\n expiresAt: number;\n createdAt: number;\n hits: number;\n}\n\nexport class JoopCacheService {\n private _store = new Map<string, CacheEntry<unknown>>();\n private _defaultTtl: number;\n private _maxSize: number;\n private _hits$ = new JoopBehaviorSubject<number>(0);\n private _totalHits = 0;\n\n constructor(options: JoopCacheOptions = {}) {\n this._defaultTtl = options.ttlMs ?? 5 * 60 * 1000; // 5 min default\n this._maxSize = options.maxSize ?? 500;\n }\n\n set<T>(key: string, value: T, ttlMs?: number): void {\n if (this._store.size >= this._maxSize && !this._store.has(key)) {\n this._evictOldest();\n }\n this._store.set(key, {\n value,\n expiresAt: Date.now() + (ttlMs ?? this._defaultTtl),\n createdAt: Date.now(),\n hits: 0,\n });\n }\n\n get<T>(key: string): T | null {\n const entry = this._store.get(key) as CacheEntry<T> | undefined;\n if (!entry) return null;\n if (Date.now() > entry.expiresAt) { this._store.delete(key); return null; }\n entry.hits++;\n this._totalHits++;\n this._hits$.next(this._totalHits);\n return entry.value;\n }\n\n has(key: string): boolean {\n const entry = this._store.get(key);\n if (!entry) return false;\n if (Date.now() > entry.expiresAt) { this._store.delete(key); return false; }\n return true;\n }\n\n delete(key: string): boolean { return this._store.delete(key); }\n\n clear(): void { this._store.clear(); }\n\n /** Return cached value or compute+store it */\n async getOrSet<T>(key: string, factory: () => T | Promise<T>, ttlMs?: number): Promise<T> {\n const cached = this.get<T>(key);\n if (cached !== null) return cached;\n const value = await factory();\n this.set(key, value, ttlMs);\n return value;\n }\n\n /** Remove all expired entries and return count removed */\n prune(): number {\n const now = Date.now();\n let removed = 0;\n for (const [k, v] of this._store) {\n if (now > v.expiresAt) { this._store.delete(k); removed++; }\n }\n return removed;\n }\n\n /** Return remaining TTL in ms for a key, or -1 if missing/expired */\n ttl(key: string): number {\n const entry = this._store.get(key);\n if (!entry) return -1;\n const remaining = entry.expiresAt - Date.now();\n return remaining > 0 ? remaining : -1;\n }\n\n /** Extend TTL of an existing entry */\n extend(key: string, ttlMs: number): boolean {\n const entry = this._store.get(key);\n if (!entry || Date.now() > entry.expiresAt) return false;\n entry.expiresAt = Date.now() + ttlMs;\n return true;\n }\n\n size(): number { return this._store.size; }\n keys(): string[] { return [...this._store.keys()]; }\n hits$() { return this._hits$.asObservable(); }\n totalHits(): number { return this._totalHits; }\n\n stats(): { size: number; maxSize: number; totalHits: number; defaultTtlMs: number } {\n return { size: this._store.size, maxSize: this._maxSize, totalHits: this._totalHits, defaultTtlMs: this._defaultTtl };\n }\n\n private _evictOldest(): void {\n let oldestKey = '';\n let oldestTime = Infinity;\n for (const [k, v] of this._store) {\n if (v.createdAt < oldestTime) { oldestTime = v.createdAt; oldestKey = k; }\n }\n if (oldestKey) this._store.delete(oldestKey);\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/events/index.ts","../../src/cache/cache.service.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 * 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 interface JoopCacheOptions {\n ttlMs?: number;\n maxSize?: number;\n}\n\ninterface CacheEntry<T> {\n value: T;\n expiresAt: number;\n createdAt: number;\n hits: number;\n}\n\nexport class JoopCacheService {\n private _store = new Map<string, CacheEntry<unknown>>();\n private _defaultTtl: number;\n private _maxSize: number;\n private _hits$ = new JoopBehaviorSubject<number>(0);\n private _totalHits = 0;\n\n constructor(options: JoopCacheOptions = {}) {\n this._defaultTtl = options.ttlMs ?? 5 * 60 * 1000; // 5 min default\n this._maxSize = options.maxSize ?? 500;\n }\n\n set<T>(key: string, value: T, ttlMs?: number): void {\n if (this._store.size >= this._maxSize && !this._store.has(key)) {\n this._evictOldest();\n }\n this._store.set(key, {\n value,\n expiresAt: Date.now() + (ttlMs ?? this._defaultTtl),\n createdAt: Date.now(),\n hits: 0,\n });\n }\n\n get<T>(key: string): T | null {\n const entry = this._store.get(key) as CacheEntry<T> | undefined;\n if (!entry) return null;\n if (Date.now() > entry.expiresAt) { this._store.delete(key); return null; }\n entry.hits++;\n this._totalHits++;\n this._hits$.next(this._totalHits);\n return entry.value;\n }\n\n has(key: string): boolean {\n const entry = this._store.get(key);\n if (!entry) return false;\n if (Date.now() > entry.expiresAt) { this._store.delete(key); return false; }\n return true;\n }\n\n delete(key: string): boolean { return this._store.delete(key); }\n\n clear(): void { this._store.clear(); }\n\n /** Return cached value or compute+store it */\n async getOrSet<T>(key: string, factory: () => T | Promise<T>, ttlMs?: number): Promise<T> {\n const cached = this.get<T>(key);\n if (cached !== null) return cached;\n const value = await factory();\n this.set(key, value, ttlMs);\n return value;\n }\n\n /** Remove all expired entries and return count removed */\n prune(): number {\n const now = Date.now();\n let removed = 0;\n for (const [k, v] of this._store) {\n if (now > v.expiresAt) { this._store.delete(k); removed++; }\n }\n return removed;\n }\n\n /** Return remaining TTL in ms for a key, or -1 if missing/expired */\n ttl(key: string): number {\n const entry = this._store.get(key);\n if (!entry) return -1;\n const remaining = entry.expiresAt - Date.now();\n return remaining > 0 ? remaining : -1;\n }\n\n /** Extend TTL of an existing entry */\n extend(key: string, ttlMs: number): boolean {\n const entry = this._store.get(key);\n if (!entry || Date.now() > entry.expiresAt) return false;\n entry.expiresAt = Date.now() + ttlMs;\n return true;\n }\n\n size(): number { return this._store.size; }\n keys(): string[] { return [...this._store.keys()]; }\n hits$() { return this._hits$.asObservable(); }\n totalHits(): number { return this._totalHits; }\n\n stats(): { size: number; maxSize: number; totalHits: number; defaultTtlMs: number } {\n return { size: this._store.size, maxSize: this._maxSize, totalHits: this._totalHits, defaultTtlMs: this._defaultTtl };\n }\n\n private _evictOldest(): void {\n let oldestKey = '';\n let oldestTime = Infinity;\n for (const [k, v] of this._store) {\n if (v.createdAt < oldestTime) { oldestTime = v.createdAt; oldestKey = k; }\n }\n if (oldestKey) this._store.delete(oldestKey);\n }\n}\n"]}
@@ -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() {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/events/index.ts","../../src/cache/cache.service.ts"],"names":[],"mappings":"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","file":"index.mjs","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 interface JoopCacheOptions {\n ttlMs?: number;\n maxSize?: number;\n}\n\ninterface CacheEntry<T> {\n value: T;\n expiresAt: number;\n createdAt: number;\n hits: number;\n}\n\nexport class JoopCacheService {\n private _store = new Map<string, CacheEntry<unknown>>();\n private _defaultTtl: number;\n private _maxSize: number;\n private _hits$ = new JoopBehaviorSubject<number>(0);\n private _totalHits = 0;\n\n constructor(options: JoopCacheOptions = {}) {\n this._defaultTtl = options.ttlMs ?? 5 * 60 * 1000; // 5 min default\n this._maxSize = options.maxSize ?? 500;\n }\n\n set<T>(key: string, value: T, ttlMs?: number): void {\n if (this._store.size >= this._maxSize && !this._store.has(key)) {\n this._evictOldest();\n }\n this._store.set(key, {\n value,\n expiresAt: Date.now() + (ttlMs ?? this._defaultTtl),\n createdAt: Date.now(),\n hits: 0,\n });\n }\n\n get<T>(key: string): T | null {\n const entry = this._store.get(key) as CacheEntry<T> | undefined;\n if (!entry) return null;\n if (Date.now() > entry.expiresAt) { this._store.delete(key); return null; }\n entry.hits++;\n this._totalHits++;\n this._hits$.next(this._totalHits);\n return entry.value;\n }\n\n has(key: string): boolean {\n const entry = this._store.get(key);\n if (!entry) return false;\n if (Date.now() > entry.expiresAt) { this._store.delete(key); return false; }\n return true;\n }\n\n delete(key: string): boolean { return this._store.delete(key); }\n\n clear(): void { this._store.clear(); }\n\n /** Return cached value or compute+store it */\n async getOrSet<T>(key: string, factory: () => T | Promise<T>, ttlMs?: number): Promise<T> {\n const cached = this.get<T>(key);\n if (cached !== null) return cached;\n const value = await factory();\n this.set(key, value, ttlMs);\n return value;\n }\n\n /** Remove all expired entries and return count removed */\n prune(): number {\n const now = Date.now();\n let removed = 0;\n for (const [k, v] of this._store) {\n if (now > v.expiresAt) { this._store.delete(k); removed++; }\n }\n return removed;\n }\n\n /** Return remaining TTL in ms for a key, or -1 if missing/expired */\n ttl(key: string): number {\n const entry = this._store.get(key);\n if (!entry) return -1;\n const remaining = entry.expiresAt - Date.now();\n return remaining > 0 ? remaining : -1;\n }\n\n /** Extend TTL of an existing entry */\n extend(key: string, ttlMs: number): boolean {\n const entry = this._store.get(key);\n if (!entry || Date.now() > entry.expiresAt) return false;\n entry.expiresAt = Date.now() + ttlMs;\n return true;\n }\n\n size(): number { return this._store.size; }\n keys(): string[] { return [...this._store.keys()]; }\n hits$() { return this._hits$.asObservable(); }\n totalHits(): number { return this._totalHits; }\n\n stats(): { size: number; maxSize: number; totalHits: number; defaultTtlMs: number } {\n return { size: this._store.size, maxSize: this._maxSize, totalHits: this._totalHits, defaultTtlMs: this._defaultTtl };\n }\n\n private _evictOldest(): void {\n let oldestKey = '';\n let oldestTime = Infinity;\n for (const [k, v] of this._store) {\n if (v.createdAt < oldestTime) { oldestTime = v.createdAt; oldestKey = k; }\n }\n if (oldestKey) this._store.delete(oldestKey);\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/events/index.ts","../../src/cache/cache.service.ts"],"names":[],"mappings":"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","file":"index.mjs","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 interface JoopCacheOptions {\n ttlMs?: number;\n maxSize?: number;\n}\n\ninterface CacheEntry<T> {\n value: T;\n expiresAt: number;\n createdAt: number;\n hits: number;\n}\n\nexport class JoopCacheService {\n private _store = new Map<string, CacheEntry<unknown>>();\n private _defaultTtl: number;\n private _maxSize: number;\n private _hits$ = new JoopBehaviorSubject<number>(0);\n private _totalHits = 0;\n\n constructor(options: JoopCacheOptions = {}) {\n this._defaultTtl = options.ttlMs ?? 5 * 60 * 1000; // 5 min default\n this._maxSize = options.maxSize ?? 500;\n }\n\n set<T>(key: string, value: T, ttlMs?: number): void {\n if (this._store.size >= this._maxSize && !this._store.has(key)) {\n this._evictOldest();\n }\n this._store.set(key, {\n value,\n expiresAt: Date.now() + (ttlMs ?? this._defaultTtl),\n createdAt: Date.now(),\n hits: 0,\n });\n }\n\n get<T>(key: string): T | null {\n const entry = this._store.get(key) as CacheEntry<T> | undefined;\n if (!entry) return null;\n if (Date.now() > entry.expiresAt) { this._store.delete(key); return null; }\n entry.hits++;\n this._totalHits++;\n this._hits$.next(this._totalHits);\n return entry.value;\n }\n\n has(key: string): boolean {\n const entry = this._store.get(key);\n if (!entry) return false;\n if (Date.now() > entry.expiresAt) { this._store.delete(key); return false; }\n return true;\n }\n\n delete(key: string): boolean { return this._store.delete(key); }\n\n clear(): void { this._store.clear(); }\n\n /** Return cached value or compute+store it */\n async getOrSet<T>(key: string, factory: () => T | Promise<T>, ttlMs?: number): Promise<T> {\n const cached = this.get<T>(key);\n if (cached !== null) return cached;\n const value = await factory();\n this.set(key, value, ttlMs);\n return value;\n }\n\n /** Remove all expired entries and return count removed */\n prune(): number {\n const now = Date.now();\n let removed = 0;\n for (const [k, v] of this._store) {\n if (now > v.expiresAt) { this._store.delete(k); removed++; }\n }\n return removed;\n }\n\n /** Return remaining TTL in ms for a key, or -1 if missing/expired */\n ttl(key: string): number {\n const entry = this._store.get(key);\n if (!entry) return -1;\n const remaining = entry.expiresAt - Date.now();\n return remaining > 0 ? remaining : -1;\n }\n\n /** Extend TTL of an existing entry */\n extend(key: string, ttlMs: number): boolean {\n const entry = this._store.get(key);\n if (!entry || Date.now() > entry.expiresAt) return false;\n entry.expiresAt = Date.now() + ttlMs;\n return true;\n }\n\n size(): number { return this._store.size; }\n keys(): string[] { return [...this._store.keys()]; }\n hits$() { return this._hits$.asObservable(); }\n totalHits(): number { return this._totalHits; }\n\n stats(): { size: number; maxSize: number; totalHits: number; defaultTtlMs: number } {\n return { size: this._store.size, maxSize: this._maxSize, totalHits: this._totalHits, defaultTtlMs: this._defaultTtl };\n }\n\n private _evictOldest(): void {\n let oldestKey = '';\n let oldestTime = Infinity;\n for (const [k, v] of this._store) {\n if (v.createdAt < oldestTime) { oldestTime = v.createdAt; oldestKey = k; }\n }\n if (oldestKey) this._store.delete(oldestKey);\n }\n}\n"]}
@@ -1,4 +1,3 @@
1
- import './config.service-CrCvI-JS.js';
2
1
  import { e as JoopLoggerConfig } from './config.models-Cqg04fAQ.js';
3
2
  import { J as JoopObservable } from './index-Dz0gOur2.js';
4
3
 
@@ -199,4 +198,4 @@ declare class JoopConsentService {
199
198
  private _persist;
200
199
  }
201
200
 
202
- export { JoopLogger as J, JoopPluginService as a, JoopEnvironmentService as b, JoopHealthService as c, JoopFeatureFlagService as d, JoopRateLimiter as e, JoopConsentService as f, type JoopConsentCategory as g, type JoopConsentState as h, type JoopEnvironmentName as i, type JoopEnvironmentProfile as j, type JoopFlagContext as k, type JoopFlagDefinition as l, type JoopHealthCheck as m, type JoopHealthCheckResult as n, type JoopLogEntry as o, type JoopLoggerOptions as p, type JoopPlugin as q, JoopTokenBucket as r, type JoopTokenBucketConfig as s, logger as t };
201
+ export { type JoopConsentCategory as J, JoopConsentService as a, type JoopConsentState as b, type JoopEnvironmentName as c, type JoopEnvironmentProfile as d, JoopEnvironmentService as e, JoopFeatureFlagService as f, type JoopFlagContext as g, type JoopFlagDefinition as h, type JoopHealthCheck as i, type JoopHealthCheckResult as j, JoopHealthService as k, JoopLogger as l, type JoopPlugin as m, JoopPluginService as n, JoopRateLimiter as o, JoopTokenBucket as p, type JoopTokenBucketConfig as q, logger as r, type JoopLogEntry as s, type JoopLoggerOptions as t };
@@ -1,4 +1,3 @@
1
- import './config.service-Cz4QQLlf.mjs';
2
1
  import { e as JoopLoggerConfig } from './config.models-Cqg04fAQ.mjs';
3
2
  import { J as JoopObservable } from './index-Dz0gOur2.mjs';
4
3
 
@@ -199,4 +198,4 @@ declare class JoopConsentService {
199
198
  private _persist;
200
199
  }
201
200
 
202
- export { JoopLogger as J, JoopPluginService as a, JoopEnvironmentService as b, JoopHealthService as c, JoopFeatureFlagService as d, JoopRateLimiter as e, JoopConsentService as f, type JoopConsentCategory as g, type JoopConsentState as h, type JoopEnvironmentName as i, type JoopEnvironmentProfile as j, type JoopFlagContext as k, type JoopFlagDefinition as l, type JoopHealthCheck as m, type JoopHealthCheckResult as n, type JoopLogEntry as o, type JoopLoggerOptions as p, type JoopPlugin as q, JoopTokenBucket as r, type JoopTokenBucketConfig as s, logger as t };
201
+ export { type JoopConsentCategory as J, JoopConsentService as a, type JoopConsentState as b, type JoopEnvironmentName as c, type JoopEnvironmentProfile as d, JoopEnvironmentService as e, JoopFeatureFlagService as f, type JoopFlagContext as g, type JoopFlagDefinition as h, type JoopHealthCheck as i, type JoopHealthCheckResult as j, JoopHealthService as k, JoopLogger as l, type JoopPlugin as m, JoopPluginService as n, JoopRateLimiter as o, JoopTokenBucket as p, type JoopTokenBucketConfig as q, logger as r, type JoopLogEntry as s, type JoopLoggerOptions as t };
@@ -1,4 +1,37 @@
1
1
  export { J as JoopConfigService, c as configService } from '../config.service-Cz4QQLlf.mjs';
2
- export { g as JoopConsentCategory, f as JoopConsentService, h as JoopConsentState, i as JoopEnvironmentName, j as JoopEnvironmentProfile, b as JoopEnvironmentService, d as JoopFeatureFlagService, k as JoopFlagContext, l as JoopFlagDefinition, m as JoopHealthCheck, n as JoopHealthCheckResult, c as JoopHealthService, J as JoopLogger, q as JoopPlugin, a as JoopPluginService, e as JoopRateLimiter, r as JoopTokenBucket, s as JoopTokenBucketConfig, t as logger } from '../index-B_ksKpS1.mjs';
2
+ export { J as JoopConsentCategory, a as JoopConsentService, b as JoopConsentState, c as JoopEnvironmentName, d as JoopEnvironmentProfile, e as JoopEnvironmentService, f as JoopFeatureFlagService, g as JoopFlagContext, h as JoopFlagDefinition, i as JoopHealthCheck, j as JoopHealthCheckResult, k as JoopHealthService, l as JoopLogger, m as JoopPlugin, n as JoopPluginService, o as JoopRateLimiter, p as JoopTokenBucket, q as JoopTokenBucketConfig, r as logger } from '../consent.service-DQ-JAEJx.mjs';
3
3
  import '../config.models-Cqg04fAQ.mjs';
4
4
  import '../index-Dz0gOur2.mjs';
5
+
6
+ /**
7
+ * Federation-safe singletons.
8
+ *
9
+ * When joopjs is bundled into multiple micro-frontends (Module Federation,
10
+ * import maps, or simply two npm copies at different versions) and is NOT
11
+ * shared as a singleton, every copy creates its own logger / config / event
12
+ * bus. The consequence is silent and nasty: an event emitted in MFE-A never
13
+ * reaches a subscriber in MFE-B, and config loaded by the host is invisible
14
+ * to a remote.
15
+ *
16
+ * Routing module-level singletons through a versioned `globalThis` registry
17
+ * makes duplicate copies converge on the FIRST instance that registered, so
18
+ * state stays shared even when the bytes are duplicated. The version is keyed
19
+ * to the major version — copies across a major boundary intentionally do NOT
20
+ * share, since their contracts may differ.
21
+ *
22
+ * This is a runtime safety net. The correct primary fix is still to share
23
+ * joopjs as a federation singleton:
24
+ * shared: { joopjs: { singleton: true, requiredVersion: '^2.0.0' } }
25
+ */
26
+ /**
27
+ * Returns the process-wide singleton for `key`, creating it via `factory` on
28
+ * first use. Subsequent calls — including from other bundled copies of joopjs —
29
+ * return the same instance.
30
+ */
31
+ declare function globalSingleton<T>(key: string, factory: () => T): T;
32
+ /** How many distinct bundled copies of joopjs have loaded in this runtime (1 is healthy). */
33
+ declare function joopCopyCount(): number;
34
+ /** Suppress the multiple-copies console warning (e.g. when duplication is intentional in dev). */
35
+ declare function silenceDuplicateCopyWarning(): void;
36
+
37
+ export { globalSingleton, joopCopyCount, silenceDuplicateCopyWarning };
@@ -1,4 +1,37 @@
1
1
  export { J as JoopConfigService, c as configService } from '../config.service-CrCvI-JS.js';
2
- export { g as JoopConsentCategory, f as JoopConsentService, h as JoopConsentState, i as JoopEnvironmentName, j as JoopEnvironmentProfile, b as JoopEnvironmentService, d as JoopFeatureFlagService, k as JoopFlagContext, l as JoopFlagDefinition, m as JoopHealthCheck, n as JoopHealthCheckResult, c as JoopHealthService, J as JoopLogger, q as JoopPlugin, a as JoopPluginService, e as JoopRateLimiter, r as JoopTokenBucket, s as JoopTokenBucketConfig, t as logger } from '../index-DFqEoX_l.js';
2
+ export { J as JoopConsentCategory, a as JoopConsentService, b as JoopConsentState, c as JoopEnvironmentName, d as JoopEnvironmentProfile, e as JoopEnvironmentService, f as JoopFeatureFlagService, g as JoopFlagContext, h as JoopFlagDefinition, i as JoopHealthCheck, j as JoopHealthCheckResult, k as JoopHealthService, l as JoopLogger, m as JoopPlugin, n as JoopPluginService, o as JoopRateLimiter, p as JoopTokenBucket, q as JoopTokenBucketConfig, r as logger } from '../consent.service-CIHNtx9h.js';
3
3
  import '../config.models-Cqg04fAQ.js';
4
4
  import '../index-Dz0gOur2.js';
5
+
6
+ /**
7
+ * Federation-safe singletons.
8
+ *
9
+ * When joopjs is bundled into multiple micro-frontends (Module Federation,
10
+ * import maps, or simply two npm copies at different versions) and is NOT
11
+ * shared as a singleton, every copy creates its own logger / config / event
12
+ * bus. The consequence is silent and nasty: an event emitted in MFE-A never
13
+ * reaches a subscriber in MFE-B, and config loaded by the host is invisible
14
+ * to a remote.
15
+ *
16
+ * Routing module-level singletons through a versioned `globalThis` registry
17
+ * makes duplicate copies converge on the FIRST instance that registered, so
18
+ * state stays shared even when the bytes are duplicated. The version is keyed
19
+ * to the major version — copies across a major boundary intentionally do NOT
20
+ * share, since their contracts may differ.
21
+ *
22
+ * This is a runtime safety net. The correct primary fix is still to share
23
+ * joopjs as a federation singleton:
24
+ * shared: { joopjs: { singleton: true, requiredVersion: '^2.0.0' } }
25
+ */
26
+ /**
27
+ * Returns the process-wide singleton for `key`, creating it via `factory` on
28
+ * first use. Subsequent calls — including from other bundled copies of joopjs —
29
+ * return the same instance.
30
+ */
31
+ declare function globalSingleton<T>(key: string, factory: () => T): T;
32
+ /** How many distinct bundled copies of joopjs have loaded in this runtime (1 is healthy). */
33
+ declare function joopCopyCount(): number;
34
+ /** Suppress the multiple-copies console warning (e.g. when duplication is intentional in dev). */
35
+ declare function silenceDuplicateCopyWarning(): void;
36
+
37
+ export { globalSingleton, joopCopyCount, silenceDuplicateCopyWarning };
@@ -8,6 +8,42 @@ function removeTrailingSlash(url) {
8
8
  return url?.endsWith("/") ? url.slice(0, -1) : url;
9
9
  }
10
10
 
11
+ // src/core/global-singleton.ts
12
+ var MAJOR = "2";
13
+ var REGISTRY_KEY = /* @__PURE__ */ Symbol.for(`joopjs.singletons.v${MAJOR}`);
14
+ function getRegistry() {
15
+ const g = globalThis;
16
+ let reg = g[REGISTRY_KEY];
17
+ if (!reg) {
18
+ reg = { major: MAJOR, instances: /* @__PURE__ */ new Map(), copies: 0, warned: false, warningsSilenced: false };
19
+ g[REGISTRY_KEY] = reg;
20
+ }
21
+ return reg;
22
+ }
23
+ (() => {
24
+ const reg = getRegistry();
25
+ reg.copies += 1;
26
+ if (reg.copies > 1 && !reg.warned && !reg.warningsSilenced) {
27
+ reg.warned = true;
28
+ console.warn(
29
+ `[joopjs] ${reg.copies} copies of joopjs detected in one runtime. Singletons (logger, config, event bus, \u2026) are being de-duplicated via globalThis so state stays shared, but you are shipping duplicate bytes. Share joopjs as a federation singleton: shared: { joopjs: { singleton: true, requiredVersion: '^2.0.0' } }. Call silenceDuplicateCopyWarning() to suppress this.`
30
+ );
31
+ }
32
+ })();
33
+ function globalSingleton(key, factory) {
34
+ const reg = getRegistry();
35
+ if (!reg.instances.has(key)) {
36
+ reg.instances.set(key, factory());
37
+ }
38
+ return reg.instances.get(key);
39
+ }
40
+ function joopCopyCount() {
41
+ return getRegistry().copies;
42
+ }
43
+ function silenceDuplicateCopyWarning() {
44
+ getRegistry().warningsSilenced = true;
45
+ }
46
+
11
47
  // src/core/config/config.service.ts
12
48
  var JoopConfigService = class {
13
49
  _config = null;
@@ -77,9 +113,12 @@ var JoopConfigService = class {
77
113
  return this._config;
78
114
  }
79
115
  };
80
- var configService = new JoopConfigService();
116
+ var configService = globalSingleton("config", () => new JoopConfigService());
81
117
 
82
118
  // src/events/index.ts
119
+ var _onListenerError = (error) => {
120
+ console.error("[joopjs] a subject subscriber threw during emission:", error);
121
+ };
83
122
  var JoopSubject = class {
84
123
  _listeners = [];
85
124
  subscribe(listener) {
@@ -89,8 +128,13 @@ var JoopSubject = class {
89
128
  };
90
129
  }
91
130
  next(value) {
92
- for (const listener of this._listeners) {
93
- listener(value);
131
+ const listeners = this._listeners.slice();
132
+ for (const listener of listeners) {
133
+ try {
134
+ listener(value);
135
+ } catch (error) {
136
+ _onListenerError(error);
137
+ }
94
138
  }
95
139
  }
96
140
  asObservable() {
@@ -111,7 +155,11 @@ var JoopBehaviorSubject = class extends JoopSubject {
111
155
  super.next(value);
112
156
  }
113
157
  subscribe(listener) {
114
- listener(this._value);
158
+ try {
159
+ listener(this._value);
160
+ } catch (error) {
161
+ _onListenerError(error);
162
+ }
115
163
  return super.subscribe(listener);
116
164
  }
117
165
  asObservable() {
@@ -198,7 +246,7 @@ var JoopLogger = class {
198
246
  }
199
247
  }
200
248
  };
201
- var logger = new JoopLogger();
249
+ var logger = globalSingleton("logger", () => new JoopLogger());
202
250
 
203
251
  // src/core/plugin.service.ts
204
252
  var JoopPluginService = class {
@@ -626,6 +674,9 @@ exports.JoopPluginService = JoopPluginService;
626
674
  exports.JoopRateLimiter = JoopRateLimiter;
627
675
  exports.JoopTokenBucket = JoopTokenBucket;
628
676
  exports.configService = configService;
677
+ exports.globalSingleton = globalSingleton;
678
+ exports.joopCopyCount = joopCopyCount;
629
679
  exports.logger = logger;
680
+ exports.silenceDuplicateCopyWarning = silenceDuplicateCopyWarning;
630
681
  //# sourceMappingURL=index.js.map
631
682
  //# sourceMappingURL=index.js.map