joopjs 2.0.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 (259) hide show
  1. package/CHANGELOG.md +678 -0
  2. package/README.md +583 -0
  3. package/dist/a11y.service-C-DQQfgO.d.mts +143 -0
  4. package/dist/a11y.service-CauEJrJe.d.ts +143 -0
  5. package/dist/adapters-B6slG6hQ.d.mts +84 -0
  6. package/dist/adapters-B6slG6hQ.d.ts +84 -0
  7. package/dist/aes.service-CkoupAww.d.mts +95 -0
  8. package/dist/aes.service-CkoupAww.d.ts +95 -0
  9. package/dist/ai/index.d.mts +99 -0
  10. package/dist/ai/index.d.ts +99 -0
  11. package/dist/ai/index.js +307 -0
  12. package/dist/ai/index.js.map +1 -0
  13. package/dist/ai/index.mjs +304 -0
  14. package/dist/ai/index.mjs.map +1 -0
  15. package/dist/analytics/index.d.mts +42 -0
  16. package/dist/analytics/index.d.ts +42 -0
  17. package/dist/analytics/index.js +139 -0
  18. package/dist/analytics/index.js.map +1 -0
  19. package/dist/analytics/index.mjs +136 -0
  20. package/dist/analytics/index.mjs.map +1 -0
  21. package/dist/angular/index.d.mts +148 -0
  22. package/dist/angular/index.d.ts +148 -0
  23. package/dist/angular/index.js +122 -0
  24. package/dist/angular/index.js.map +1 -0
  25. package/dist/angular/index.mjs +101 -0
  26. package/dist/angular/index.mjs.map +1 -0
  27. package/dist/api/index.d.mts +128 -0
  28. package/dist/api/index.d.ts +128 -0
  29. package/dist/api/index.js +1358 -0
  30. package/dist/api/index.js.map +1 -0
  31. package/dist/api/index.mjs +1332 -0
  32. package/dist/api/index.mjs.map +1 -0
  33. package/dist/auth/index.d.mts +105 -0
  34. package/dist/auth/index.d.ts +105 -0
  35. package/dist/auth/index.js +989 -0
  36. package/dist/auth/index.js.map +1 -0
  37. package/dist/auth/index.mjs +979 -0
  38. package/dist/auth/index.mjs.map +1 -0
  39. package/dist/auth.service-DNVB-L4U.d.mts +16 -0
  40. package/dist/auth.service-PjUUSUIt.d.ts +16 -0
  41. package/dist/banking/index.d.mts +1530 -0
  42. package/dist/banking/index.d.ts +1530 -0
  43. package/dist/banking/index.js +4739 -0
  44. package/dist/banking/index.js.map +1 -0
  45. package/dist/banking/index.mjs +4661 -0
  46. package/dist/banking/index.mjs.map +1 -0
  47. package/dist/cache/index.d.mts +40 -0
  48. package/dist/cache/index.d.ts +40 -0
  49. package/dist/cache/index.js +174 -0
  50. package/dist/cache/index.js.map +1 -0
  51. package/dist/cache/index.mjs +172 -0
  52. package/dist/cache/index.mjs.map +1 -0
  53. package/dist/client-profile.service-BuPeXVp5.d.mts +28 -0
  54. package/dist/client-profile.service-D5bRRYQp.d.ts +28 -0
  55. package/dist/config.models-Cqg04fAQ.d.mts +84 -0
  56. package/dist/config.models-Cqg04fAQ.d.ts +84 -0
  57. package/dist/config.service-CrCvI-JS.d.ts +31 -0
  58. package/dist/config.service-Cz4QQLlf.d.mts +31 -0
  59. package/dist/core/index.d.mts +4 -0
  60. package/dist/core/index.d.ts +4 -0
  61. package/dist/core/index.js +631 -0
  62. package/dist/core/index.js.map +1 -0
  63. package/dist/core/index.mjs +619 -0
  64. package/dist/core/index.mjs.map +1 -0
  65. package/dist/crypto-utils-DriNhLdx.d.mts +30 -0
  66. package/dist/crypto-utils-DriNhLdx.d.ts +30 -0
  67. package/dist/data-storage.service-DT6xaTxE.d.ts +51 -0
  68. package/dist/data-storage.service-LvhGRCmw.d.mts +51 -0
  69. package/dist/deeplink/index.d.mts +39 -0
  70. package/dist/deeplink/index.d.ts +39 -0
  71. package/dist/deeplink/index.js +268 -0
  72. package/dist/deeplink/index.js.map +1 -0
  73. package/dist/deeplink/index.mjs +265 -0
  74. package/dist/deeplink/index.mjs.map +1 -0
  75. package/dist/deeplink.service-Ctd5u243.d.mts +35 -0
  76. package/dist/deeplink.service-uUuTnY9_.d.ts +35 -0
  77. package/dist/dev/index.d.mts +20 -0
  78. package/dist/dev/index.d.ts +20 -0
  79. package/dist/dev/index.js +51 -0
  80. package/dist/dev/index.js.map +1 -0
  81. package/dist/dev/index.mjs +49 -0
  82. package/dist/dev/index.mjs.map +1 -0
  83. package/dist/device/index.d.mts +108 -0
  84. package/dist/device/index.d.ts +108 -0
  85. package/dist/device/index.js +960 -0
  86. package/dist/device/index.js.map +1 -0
  87. package/dist/device/index.mjs +951 -0
  88. package/dist/device/index.mjs.map +1 -0
  89. package/dist/differential-privacy-BcAv1G80.d.mts +210 -0
  90. package/dist/differential-privacy-C8mAUjZr.d.ts +210 -0
  91. package/dist/encryption/index.d.mts +75 -0
  92. package/dist/encryption/index.d.ts +75 -0
  93. package/dist/encryption/index.js +605 -0
  94. package/dist/encryption/index.js.map +1 -0
  95. package/dist/encryption/index.mjs +598 -0
  96. package/dist/encryption/index.mjs.map +1 -0
  97. package/dist/form-validator-3tkmzr_o.d.mts +72 -0
  98. package/dist/form-validator-3tkmzr_o.d.ts +72 -0
  99. package/dist/forms/index.d.mts +59 -0
  100. package/dist/forms/index.d.ts +59 -0
  101. package/dist/forms/index.js +446 -0
  102. package/dist/forms/index.js.map +1 -0
  103. package/dist/forms/index.mjs +442 -0
  104. package/dist/forms/index.mjs.map +1 -0
  105. package/dist/i18n/index.d.mts +37 -0
  106. package/dist/i18n/index.d.ts +37 -0
  107. package/dist/i18n/index.js +147 -0
  108. package/dist/i18n/index.js.map +1 -0
  109. package/dist/i18n/index.mjs +145 -0
  110. package/dist/i18n/index.mjs.map +1 -0
  111. package/dist/idempotency.service-_6LqhivP.d.mts +372 -0
  112. package/dist/idempotency.service-eOKoISRD.d.ts +372 -0
  113. package/dist/index-B_ksKpS1.d.mts +202 -0
  114. package/dist/index-CqDKWTUP.d.mts +28 -0
  115. package/dist/index-CqDKWTUP.d.ts +28 -0
  116. package/dist/index-DFqEoX_l.d.ts +202 -0
  117. package/dist/index-Dz0gOur2.d.mts +36 -0
  118. package/dist/index-Dz0gOur2.d.ts +36 -0
  119. package/dist/index.d.mts +1336 -0
  120. package/dist/index.d.ts +1336 -0
  121. package/dist/index.js +19464 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/index.mjs +19155 -0
  124. package/dist/index.mjs.map +1 -0
  125. package/dist/india/index.d.mts +75 -0
  126. package/dist/india/index.d.ts +75 -0
  127. package/dist/india/index.js +325 -0
  128. package/dist/india/index.js.map +1 -0
  129. package/dist/india/index.mjs +303 -0
  130. package/dist/india/index.mjs.map +1 -0
  131. package/dist/joop-Bx7Iwj5p.d.mts +155 -0
  132. package/dist/joop-CA3DMeOO.d.ts +155 -0
  133. package/dist/native-bridge/index.d.mts +27 -0
  134. package/dist/native-bridge/index.d.ts +27 -0
  135. package/dist/native-bridge/index.js +98 -0
  136. package/dist/native-bridge/index.js.map +1 -0
  137. package/dist/native-bridge/index.mjs +96 -0
  138. package/dist/native-bridge/index.mjs.map +1 -0
  139. package/dist/network/index.d.mts +85 -0
  140. package/dist/network/index.d.ts +85 -0
  141. package/dist/network/index.js +454 -0
  142. package/dist/network/index.js.map +1 -0
  143. package/dist/network/index.mjs +451 -0
  144. package/dist/network/index.mjs.map +1 -0
  145. package/dist/network-monitor-BIwPSXme.d.mts +179 -0
  146. package/dist/network-monitor-Bqp2hvZr.d.ts +179 -0
  147. package/dist/notification.service-Dm4fvfZf.d.mts +25 -0
  148. package/dist/notification.service-tEMKatWJ.d.ts +25 -0
  149. package/dist/observability/index.d.mts +179 -0
  150. package/dist/observability/index.d.ts +179 -0
  151. package/dist/observability/index.js +559 -0
  152. package/dist/observability/index.js.map +1 -0
  153. package/dist/observability/index.mjs +552 -0
  154. package/dist/observability/index.mjs.map +1 -0
  155. package/dist/oidc-client-DIJcClmB.d.mts +190 -0
  156. package/dist/oidc-client-DxhyE59t.d.ts +190 -0
  157. package/dist/platform/index.d.mts +73 -0
  158. package/dist/platform/index.d.ts +73 -0
  159. package/dist/platform/index.js +127 -0
  160. package/dist/platform/index.js.map +1 -0
  161. package/dist/platform/index.mjs +125 -0
  162. package/dist/platform/index.mjs.map +1 -0
  163. package/dist/pwa/index.d.mts +31 -0
  164. package/dist/pwa/index.d.ts +31 -0
  165. package/dist/pwa/index.js +247 -0
  166. package/dist/pwa/index.js.map +1 -0
  167. package/dist/pwa/index.mjs +244 -0
  168. package/dist/pwa/index.mjs.map +1 -0
  169. package/dist/react/index.d.mts +133 -0
  170. package/dist/react/index.d.ts +133 -0
  171. package/dist/react/index.js +632 -0
  172. package/dist/react/index.js.map +1 -0
  173. package/dist/react/index.mjs +630 -0
  174. package/dist/react/index.mjs.map +1 -0
  175. package/dist/router/index.d.mts +39 -0
  176. package/dist/router/index.d.ts +39 -0
  177. package/dist/router/index.js +168 -0
  178. package/dist/router/index.js.map +1 -0
  179. package/dist/router/index.mjs +166 -0
  180. package/dist/router/index.mjs.map +1 -0
  181. package/dist/security/index.d.mts +206 -0
  182. package/dist/security/index.d.ts +206 -0
  183. package/dist/security/index.js +1297 -0
  184. package/dist/security/index.js.map +1 -0
  185. package/dist/security/index.mjs +1285 -0
  186. package/dist/security/index.mjs.map +1 -0
  187. package/dist/session/index.d.mts +115 -0
  188. package/dist/session/index.d.ts +115 -0
  189. package/dist/session/index.js +297 -0
  190. package/dist/session/index.js.map +1 -0
  191. package/dist/session/index.mjs +292 -0
  192. package/dist/session/index.mjs.map +1 -0
  193. package/dist/state/index.d.mts +43 -0
  194. package/dist/state/index.d.ts +43 -0
  195. package/dist/state/index.js +156 -0
  196. package/dist/state/index.js.map +1 -0
  197. package/dist/state/index.mjs +152 -0
  198. package/dist/state/index.mjs.map +1 -0
  199. package/dist/statement-parser-BHQtXwCM.d.ts +260 -0
  200. package/dist/statement-parser-C2qNmb49.d.mts +260 -0
  201. package/dist/storage/index.d.mts +40 -0
  202. package/dist/storage/index.d.ts +40 -0
  203. package/dist/storage/index.js +256 -0
  204. package/dist/storage/index.js.map +1 -0
  205. package/dist/storage/index.mjs +252 -0
  206. package/dist/storage/index.mjs.map +1 -0
  207. package/dist/sync/index.d.mts +69 -0
  208. package/dist/sync/index.d.ts +69 -0
  209. package/dist/sync/index.js +330 -0
  210. package/dist/sync/index.js.map +1 -0
  211. package/dist/sync/index.mjs +323 -0
  212. package/dist/sync/index.mjs.map +1 -0
  213. package/dist/sync-engine-DCIMRG5s.d.ts +61 -0
  214. package/dist/sync-engine-DZqyKHkK.d.mts +61 -0
  215. package/dist/theme/index.d.mts +53 -0
  216. package/dist/theme/index.d.ts +53 -0
  217. package/dist/theme/index.js +169 -0
  218. package/dist/theme/index.js.map +1 -0
  219. package/dist/theme/index.mjs +167 -0
  220. package/dist/theme/index.mjs.map +1 -0
  221. package/dist/ui/index.d.mts +66 -0
  222. package/dist/ui/index.d.ts +66 -0
  223. package/dist/ui/index.js +811 -0
  224. package/dist/ui/index.js.map +1 -0
  225. package/dist/ui/index.mjs +803 -0
  226. package/dist/ui/index.mjs.map +1 -0
  227. package/dist/utilities/index.d.mts +199 -0
  228. package/dist/utilities/index.d.ts +199 -0
  229. package/dist/utilities/index.js +1991 -0
  230. package/dist/utilities/index.js.map +1 -0
  231. package/dist/utilities/index.mjs +1923 -0
  232. package/dist/utilities/index.mjs.map +1 -0
  233. package/dist/validation/index.d.mts +60 -0
  234. package/dist/validation/index.d.ts +60 -0
  235. package/dist/validation/index.js +460 -0
  236. package/dist/validation/index.js.map +1 -0
  237. package/dist/validation/index.mjs +455 -0
  238. package/dist/validation/index.mjs.map +1 -0
  239. package/dist/vue/index.d.mts +135 -0
  240. package/dist/vue/index.d.ts +135 -0
  241. package/dist/vue/index.js +621 -0
  242. package/dist/vue/index.js.map +1 -0
  243. package/dist/vue/index.mjs +619 -0
  244. package/dist/vue/index.mjs.map +1 -0
  245. package/dist/watermark.service-Detur5tq.d.ts +235 -0
  246. package/dist/watermark.service-QNegMeQZ.d.mts +235 -0
  247. package/dist/workers/index.d.mts +42 -0
  248. package/dist/workers/index.d.ts +42 -0
  249. package/dist/workers/index.js +359 -0
  250. package/dist/workers/index.js.map +1 -0
  251. package/dist/workers/index.mjs +356 -0
  252. package/dist/workers/index.mjs.map +1 -0
  253. package/dist/workflow/index.d.mts +99 -0
  254. package/dist/workflow/index.d.ts +99 -0
  255. package/dist/workflow/index.js +282 -0
  256. package/dist/workflow/index.js.map +1 -0
  257. package/dist/workflow/index.mjs +279 -0
  258. package/dist/workflow/index.mjs.map +1 -0
  259. package/package.json +226 -0
@@ -0,0 +1,960 @@
1
+ 'use strict';
2
+
3
+ // src/device/device-fingerprint.service.ts
4
+ var JoopDeviceFingerprintService = class {
5
+ collect() {
6
+ const nav = typeof navigator !== "undefined" ? navigator : null;
7
+ const scr = typeof screen !== "undefined" ? screen : null;
8
+ return {
9
+ userAgent: nav?.userAgent ?? "",
10
+ language: nav?.language ?? "",
11
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
12
+ screenResolution: scr ? `${scr.width}x${scr.height}` : "",
13
+ colorDepth: scr?.colorDepth ?? 0,
14
+ platform: nav?.platform ?? "",
15
+ hardwareConcurrency: nav?.hardwareConcurrency ?? 0,
16
+ maxTouchPoints: nav?.maxTouchPoints ?? 0
17
+ };
18
+ }
19
+ async generate() {
20
+ const info = this.collect();
21
+ const raw = Object.values(info).join("|");
22
+ const hashBuffer = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(raw));
23
+ return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
24
+ }
25
+ async getStableId() {
26
+ const key = "__joop_did";
27
+ const stored = typeof localStorage !== "undefined" ? localStorage.getItem(key) : null;
28
+ if (stored) return stored;
29
+ const id = await this.generate();
30
+ if (typeof localStorage !== "undefined") localStorage.setItem(key, id);
31
+ return id;
32
+ }
33
+ };
34
+
35
+ // src/platform/platform.ts
36
+ var _webStorage = (store) => ({
37
+ getItem: (k) => store.getItem(k),
38
+ setItem: (k, v) => store.setItem(k, v),
39
+ removeItem: (k) => store.removeItem(k),
40
+ clear: () => store.clear()
41
+ });
42
+ var _MemStorageImpl = class {
43
+ _m = /* @__PURE__ */ new Map();
44
+ getItem(k) {
45
+ return this._m.get(k) ?? null;
46
+ }
47
+ setItem(k, v) {
48
+ this._m.set(k, v);
49
+ }
50
+ removeItem(k) {
51
+ this._m.delete(k);
52
+ }
53
+ clear() {
54
+ this._m.clear();
55
+ }
56
+ };
57
+ function _detect() {
58
+ if (typeof navigator !== "undefined" && navigator.product === "ReactNative") return "react-native";
59
+ const _proc = globalThis["process"];
60
+ if (typeof window === "undefined" && _proc?.versions?.node) return "node";
61
+ if (typeof window !== "undefined") return "web";
62
+ return "unknown";
63
+ }
64
+ var JoopPlatform = class {
65
+ static _type = _detect();
66
+ static _adapter = {};
67
+ static _memStorage = null;
68
+ /**
69
+ * Call once at app startup to configure adapters for your platform.
70
+ *
71
+ * Web (default — no call needed):
72
+ * JoopPlatform.init();
73
+ *
74
+ * React Native with react-native-mmkv:
75
+ * import { MMKV } from 'react-native-mmkv';
76
+ * const mmkv = new MMKV();
77
+ * JoopPlatform.init({ adapter: { storage: mmkv } });
78
+ *
79
+ * React Native with custom compression (fflate):
80
+ * import { gzip, ungzip } from 'fflate';
81
+ * JoopPlatform.init({
82
+ * adapter: {
83
+ * compression: {
84
+ * compress: (d, fmt) => new Promise((res, rej) => gzip(d, (e, r) => e ? rej(e) : res(r))),
85
+ * decompress: (d, fmt) => new Promise((res, rej) => ungzip(d, (e, r) => e ? rej(e) : res(r))),
86
+ * },
87
+ * },
88
+ * });
89
+ */
90
+ static init(config = {}) {
91
+ if (config.platform) this._type = config.platform;
92
+ this._adapter = { ...this._adapter, ...config.adapter ?? {} };
93
+ }
94
+ /** Current detected or overridden platform type */
95
+ static get type() {
96
+ return this._type;
97
+ }
98
+ static is(type) {
99
+ return this._type === type;
100
+ }
101
+ static isMobile() {
102
+ return this._type === "react-native";
103
+ }
104
+ static isWeb() {
105
+ return this._type === "web";
106
+ }
107
+ static isNode() {
108
+ return this._type === "node";
109
+ }
110
+ /** Raw adapter config registered via init() */
111
+ static getAdapter() {
112
+ return this._adapter;
113
+ }
114
+ /**
115
+ * Returns the best available storage adapter:
116
+ * 1. Custom adapter registered via init()
117
+ * 2. localStorage (web)
118
+ * 3. In-memory fallback (Node / RN without adapter)
119
+ */
120
+ static getStorage() {
121
+ if (this._adapter.storage) return this._adapter.storage;
122
+ if (typeof localStorage !== "undefined") return _webStorage(localStorage);
123
+ if (!this._memStorage) this._memStorage = new _MemStorageImpl();
124
+ return this._memStorage;
125
+ }
126
+ /**
127
+ * Returns the best available session-scoped storage:
128
+ * 1. Custom adapter registered via init()
129
+ * 2. sessionStorage (web)
130
+ * 3. Shared in-memory fallback
131
+ */
132
+ static getSessionStorage() {
133
+ if (this._adapter.storage) return this._adapter.storage;
134
+ if (typeof sessionStorage !== "undefined") return _webStorage(sessionStorage);
135
+ if (!this._memStorage) this._memStorage = new _MemStorageImpl();
136
+ return this._memStorage;
137
+ }
138
+ /** What APIs are available in the current environment */
139
+ static capabilities() {
140
+ return {
141
+ compression: typeof CompressionStream !== "undefined" || !!this._adapter.compression,
142
+ workers: typeof Worker !== "undefined" || !!this._adapter.worker,
143
+ cryptoSubtle: !!(typeof globalThis !== "undefined" && globalThis.crypto?.subtle),
144
+ persistentStorage: typeof localStorage !== "undefined" || !!this._adapter.storage,
145
+ dom: typeof document !== "undefined",
146
+ serviceWorker: typeof navigator !== "undefined" && "serviceWorker" in navigator
147
+ };
148
+ }
149
+ /** Reset to auto-detected defaults — useful between tests */
150
+ static _reset() {
151
+ this._type = _detect();
152
+ this._adapter = {};
153
+ this._memStorage = null;
154
+ }
155
+ };
156
+
157
+ // src/device/key-vault.ts
158
+ var JoopKeyVault = class {
159
+ _prefix;
160
+ _encKey;
161
+ _adapter;
162
+ _cryptoKey = null;
163
+ constructor(config = {}) {
164
+ this._prefix = config.prefix ?? "joop_vault_";
165
+ this._encKey = config.encryptionKey;
166
+ this._adapter = config.adapter ?? JoopPlatform.getAdapter().keyVault ?? null;
167
+ }
168
+ /** Store a secret. On web: AES-GCM encrypted if `encryptionKey` was provided. */
169
+ async set(key, value) {
170
+ if (this._adapter) return this._adapter.set(key, value);
171
+ const stored = this._encKey ? await _encrypt(value, await this._key()) : btoa(unescape(encodeURIComponent(value)));
172
+ JoopPlatform.getStorage().setItem(this._prefix + key, stored);
173
+ }
174
+ /** Retrieve a secret, or null if not found. */
175
+ async get(key) {
176
+ if (this._adapter) return this._adapter.get(key);
177
+ const raw = JoopPlatform.getStorage().getItem(this._prefix + key);
178
+ if (!raw) return null;
179
+ try {
180
+ return this._encKey ? await _decrypt(raw, await this._key()) : decodeURIComponent(escape(atob(raw)));
181
+ } catch {
182
+ return null;
183
+ }
184
+ }
185
+ async remove(key) {
186
+ if (this._adapter) return this._adapter.remove(key);
187
+ JoopPlatform.getStorage().removeItem(this._prefix + key);
188
+ }
189
+ async clear() {
190
+ if (this._adapter) return this._adapter.clear();
191
+ JoopPlatform.getStorage().clear?.();
192
+ }
193
+ async has(key) {
194
+ return await this.get(key) !== null;
195
+ }
196
+ /** Rotate the encryption key: re-encrypts all known entries with a new key. */
197
+ async rotateKey(newEncryptionKey, keys) {
198
+ if (this._adapter) throw new Error("rotateKey not supported with native adapter");
199
+ const values = await Promise.all(keys.map((k) => this.get(k)));
200
+ this._encKey = newEncryptionKey;
201
+ this._cryptoKey = null;
202
+ for (let i = 0; i < keys.length; i++) {
203
+ if (values[i] !== null) await this.set(keys[i], values[i]);
204
+ }
205
+ }
206
+ async _key() {
207
+ if (!this._cryptoKey) this._cryptoKey = await _deriveKey(this._encKey);
208
+ return this._cryptoKey;
209
+ }
210
+ };
211
+ var _SALT = new Uint8Array([
212
+ 106,
213
+ 111,
214
+ 111,
215
+ 112,
216
+ 45,
217
+ 118,
218
+ 97,
219
+ 117,
220
+ 108,
221
+ 116,
222
+ 45,
223
+ 118,
224
+ 49,
225
+ 0,
226
+ 0,
227
+ 0
228
+ ]);
229
+ async function _deriveKey(password) {
230
+ const km = await crypto.subtle.importKey(
231
+ "raw",
232
+ new TextEncoder().encode(password),
233
+ { name: "PBKDF2" },
234
+ false,
235
+ ["deriveKey"]
236
+ );
237
+ return crypto.subtle.deriveKey(
238
+ { name: "PBKDF2", salt: _SALT, iterations: 1e5, hash: "SHA-256" },
239
+ km,
240
+ { name: "AES-GCM", length: 256 },
241
+ false,
242
+ ["encrypt", "decrypt"]
243
+ );
244
+ }
245
+ async function _encrypt(value, key) {
246
+ const iv = crypto.getRandomValues(new Uint8Array(12));
247
+ const encrypted = await crypto.subtle.encrypt(
248
+ { name: "AES-GCM", iv },
249
+ key,
250
+ new TextEncoder().encode(value)
251
+ );
252
+ const combined = new Uint8Array(12 + encrypted.byteLength);
253
+ combined.set(iv);
254
+ combined.set(new Uint8Array(encrypted), 12);
255
+ return btoa(String.fromCharCode(...combined));
256
+ }
257
+ async function _decrypt(encoded, key) {
258
+ const combined = Uint8Array.from(atob(encoded), (c) => c.charCodeAt(0));
259
+ const iv = combined.slice(0, 12);
260
+ const data = combined.slice(12);
261
+ const decrypted = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, data);
262
+ return new TextDecoder().decode(decrypted);
263
+ }
264
+
265
+ // src/device/biometric-auth.ts
266
+ var JoopBiometricAuth = class {
267
+ _rpId;
268
+ _rpName;
269
+ _adapter;
270
+ constructor(config = {}) {
271
+ this._rpId = config.rpId ?? (typeof location !== "undefined" ? location.hostname : "localhost");
272
+ this._rpName = config.rpName ?? this._rpId;
273
+ this._adapter = config.adapter ?? JoopPlatform.getAdapter().biometric ?? null;
274
+ }
275
+ /** True when a platform authenticator is available. */
276
+ async isAvailable() {
277
+ if (this._adapter) return this._adapter.isAvailable();
278
+ if (typeof window === "undefined" || !window.PublicKeyCredential) return false;
279
+ if (typeof PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable !== "function") return false;
280
+ return PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
281
+ }
282
+ /**
283
+ * Enroll a new credential for the given user.
284
+ * Web: creates a WebAuthn passkey via navigator.credentials.create().
285
+ * React Native: delegates to the injected adapter.
286
+ */
287
+ async enroll(userId, displayName) {
288
+ if (this._adapter) throw new Error("Enrollment must be handled by the native adapter");
289
+ if (typeof navigator?.credentials?.create !== "function") {
290
+ throw new Error("WebAuthn not available in this environment");
291
+ }
292
+ const challenge = crypto.getRandomValues(new Uint8Array(32));
293
+ const cred = await navigator.credentials.create({
294
+ publicKey: {
295
+ challenge,
296
+ rp: { id: this._rpId, name: this._rpName },
297
+ user: { id: new TextEncoder().encode(userId), name: userId, displayName },
298
+ pubKeyCredParams: [
299
+ { type: "public-key", alg: -7 },
300
+ // ES256
301
+ { type: "public-key", alg: -257 }
302
+ // RS256
303
+ ],
304
+ authenticatorSelection: {
305
+ authenticatorAttachment: "platform",
306
+ userVerification: "required",
307
+ residentKey: "preferred"
308
+ },
309
+ timeout: 6e4,
310
+ attestation: "none"
311
+ }
312
+ });
313
+ const response = cred.response;
314
+ const pubKey = response.getPublicKey?.();
315
+ return {
316
+ credentialId: _b64url(cred.rawId),
317
+ publicKey: pubKey ? _b64url(pubKey) : void 0
318
+ };
319
+ }
320
+ /**
321
+ * Verify identity against an existing credential.
322
+ * Web: navigator.credentials.get() with WebAuthn assertion.
323
+ * React Native: delegates to the injected adapter.
324
+ */
325
+ async authenticate(options = {}) {
326
+ if (this._adapter) return this._adapter.authenticate(options.reason);
327
+ if (typeof navigator?.credentials?.get !== "function") return false;
328
+ const challenge = crypto.getRandomValues(new Uint8Array(32));
329
+ const allowCredentials = options.credentialId ? [{ type: "public-key", id: _b64urlToBytes(options.credentialId) }] : [];
330
+ try {
331
+ const cred = await navigator.credentials.get({
332
+ publicKey: {
333
+ challenge,
334
+ allowCredentials,
335
+ userVerification: "required",
336
+ timeout: options.timeout ?? 6e4
337
+ }
338
+ });
339
+ return !!cred;
340
+ } catch {
341
+ return false;
342
+ }
343
+ }
344
+ /**
345
+ * Quick check — asks the platform if biometrics can be used right now.
346
+ * Does not prompt the user.
347
+ */
348
+ async canAuthenticate() {
349
+ if (this._adapter) return this._adapter.isAvailable();
350
+ return this.isAvailable();
351
+ }
352
+ };
353
+ function _b64url(buf) {
354
+ return btoa(String.fromCharCode(...new Uint8Array(buf))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
355
+ }
356
+ function _b64urlToBytes(s) {
357
+ const padded = s.replace(/-/g, "+").replace(/_/g, "/").padEnd(
358
+ s.length + (4 - s.length % 4) % 4,
359
+ "="
360
+ );
361
+ return Uint8Array.from(atob(padded), (c) => c.charCodeAt(0));
362
+ }
363
+
364
+ // src/device/push-service.ts
365
+ var JoopPushService = class {
366
+ _cfg;
367
+ _adapter;
368
+ _messageHandlers = [];
369
+ _adapterUnsub = null;
370
+ constructor(config = {}) {
371
+ this._cfg = config;
372
+ this._adapter = config.adapter ?? JoopPlatform.getAdapter().push ?? null;
373
+ if (this._adapter) {
374
+ this._adapterUnsub = this._adapter.onMessage((p) => this._emit(p));
375
+ }
376
+ }
377
+ /** Request push notification permission from the user. */
378
+ async requestPermission() {
379
+ if (this._adapter) {
380
+ const result = await this._adapter.requestPermission();
381
+ return result;
382
+ }
383
+ if (typeof Notification === "undefined") return "unsupported";
384
+ if (Notification.permission === "granted") return "granted";
385
+ const perm = await Notification.requestPermission();
386
+ return perm;
387
+ }
388
+ /** Get the current permission status without prompting. */
389
+ getPermission() {
390
+ if (this._adapter) return "default";
391
+ if (typeof Notification === "undefined") return "unsupported";
392
+ return Notification.permission;
393
+ }
394
+ /**
395
+ * Subscribe to web push and return subscription info to send to your server.
396
+ * Requires a service worker and a VAPID key.
397
+ */
398
+ async subscribe(options) {
399
+ if (this._adapter) {
400
+ const token = await this._adapter.getToken(options?.vapidPublicKey ?? this._cfg.vapidPublicKey);
401
+ return token ? { endpoint: token, p256dh: "", auth: "" } : null;
402
+ }
403
+ if (typeof navigator?.serviceWorker === "undefined") return null;
404
+ if (Notification.permission !== "granted") return null;
405
+ const vapidKey = options?.vapidPublicKey ?? this._cfg.vapidPublicKey;
406
+ if (!vapidKey) return null;
407
+ const reg = await navigator.serviceWorker.register(this._cfg.serviceWorkerPath ?? "/sw.js");
408
+ const sub = await reg.pushManager.subscribe({
409
+ userVisibleOnly: true,
410
+ applicationServerKey: _urlBase64ToUint8(vapidKey)
411
+ });
412
+ const keys = sub.getKey ? {
413
+ p256dh: btoa(String.fromCharCode(...new Uint8Array(sub.getKey("p256dh")))),
414
+ auth: btoa(String.fromCharCode(...new Uint8Array(sub.getKey("auth"))))
415
+ } : { p256dh: "", auth: "" };
416
+ return { endpoint: sub.endpoint, ...keys };
417
+ }
418
+ /** Unsubscribe from web push. */
419
+ async unsubscribe() {
420
+ if (this._adapter) return false;
421
+ if (typeof navigator?.serviceWorker === "undefined") return false;
422
+ try {
423
+ const reg = await navigator.serviceWorker.ready;
424
+ const sub = await reg.pushManager.getSubscription();
425
+ return sub ? sub.unsubscribe() : false;
426
+ } catch {
427
+ return false;
428
+ }
429
+ }
430
+ /** Display an immediate local notification (no server push required). */
431
+ async show(payload) {
432
+ if (this._adapter) {
433
+ this._emit(payload);
434
+ return;
435
+ }
436
+ if (typeof Notification === "undefined") return;
437
+ if (Notification.permission !== "granted") return;
438
+ new Notification(payload.title ?? "", {
439
+ body: payload.body,
440
+ icon: payload.icon,
441
+ badge: payload.icon,
442
+ tag: payload.tag,
443
+ data: payload.data
444
+ });
445
+ }
446
+ /** Set the app badge count (mobile only via adapter). */
447
+ async setBadgeCount(count) {
448
+ if (this._adapter?.setBadgeCount) await this._adapter.setBadgeCount(count);
449
+ else if (typeof navigator?.setAppBadge === "function") {
450
+ await navigator.setAppBadge(count);
451
+ }
452
+ }
453
+ /** Clear the app badge. */
454
+ async clearBadge() {
455
+ if (typeof navigator?.clearAppBadge === "function") {
456
+ await navigator.clearAppBadge();
457
+ }
458
+ }
459
+ /** Register a handler for incoming push payloads (foreground messages). */
460
+ onMessage(handler) {
461
+ this._messageHandlers.push(handler);
462
+ return () => {
463
+ this._messageHandlers = this._messageHandlers.filter((h) => h !== handler);
464
+ };
465
+ }
466
+ destroy() {
467
+ this._adapterUnsub?.();
468
+ this._adapterUnsub = null;
469
+ this._messageHandlers = [];
470
+ }
471
+ _emit(payload) {
472
+ for (const h of this._messageHandlers) h(payload);
473
+ }
474
+ };
475
+ function _urlBase64ToUint8(base64String) {
476
+ const padding = "=".repeat((4 - base64String.length % 4) % 4);
477
+ const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");
478
+ const raw = atob(base64);
479
+ const out = new Uint8Array(raw.length);
480
+ for (let i = 0; i < raw.length; i++) out[i] = raw.charCodeAt(i);
481
+ return out;
482
+ }
483
+
484
+ // src/events/index.ts
485
+ var JoopSubject = class {
486
+ _listeners = [];
487
+ subscribe(listener) {
488
+ this._listeners.push(listener);
489
+ return () => {
490
+ this._listeners = this._listeners.filter((l) => l !== listener);
491
+ };
492
+ }
493
+ next(value) {
494
+ for (const listener of this._listeners) {
495
+ listener(value);
496
+ }
497
+ }
498
+ asObservable() {
499
+ return new JoopObservable((listener) => this.subscribe(listener));
500
+ }
501
+ };
502
+ var JoopBehaviorSubject = class extends JoopSubject {
503
+ _value;
504
+ constructor(initialValue) {
505
+ super();
506
+ this._value = initialValue;
507
+ }
508
+ getValue() {
509
+ return this._value;
510
+ }
511
+ next(value) {
512
+ this._value = value;
513
+ super.next(value);
514
+ }
515
+ subscribe(listener) {
516
+ listener(this._value);
517
+ return super.subscribe(listener);
518
+ }
519
+ asObservable() {
520
+ return new JoopObservable((listener) => this.subscribe(listener));
521
+ }
522
+ };
523
+ var JoopObservable = class {
524
+ constructor(_subscribeFn) {
525
+ this._subscribeFn = _subscribeFn;
526
+ }
527
+ _subscribeFn;
528
+ subscribe(listener) {
529
+ return this._subscribeFn(listener);
530
+ }
531
+ /** Returns the current value without subscribing (only meaningful for BehaviorSubject-backed observables). */
532
+ getOnce() {
533
+ let result;
534
+ const unsub = this.subscribe((v) => {
535
+ result = v;
536
+ });
537
+ unsub();
538
+ return result;
539
+ }
540
+ };
541
+
542
+ // src/device/app-lifecycle.ts
543
+ var JoopAppLifecycle = class {
544
+ _state$ = new JoopBehaviorSubject("unknown");
545
+ _cleanup = [];
546
+ _adapter;
547
+ constructor(config = {}) {
548
+ this._adapter = config.adapter ?? JoopPlatform.getAdapter().lifecycle ?? null;
549
+ this._init();
550
+ }
551
+ _init() {
552
+ if (this._adapter) {
553
+ this._state$.next(this._adapter.getState());
554
+ const unsub = this._adapter.onChange((s) => this._state$.next(s));
555
+ this._cleanup.push(unsub);
556
+ return;
557
+ }
558
+ if (typeof document === "undefined") {
559
+ this._state$.next("unknown");
560
+ return;
561
+ }
562
+ this._state$.next(document.hidden ? "background" : "active");
563
+ const onVisibility = () => this._state$.next(document.hidden ? "background" : "active");
564
+ const onFocus = () => this._state$.next("active");
565
+ const onBlur = () => this._state$.next("background");
566
+ document.addEventListener("visibilitychange", onVisibility);
567
+ window.addEventListener("focus", onFocus);
568
+ window.addEventListener("blur", onBlur);
569
+ this._cleanup.push(
570
+ () => document.removeEventListener("visibilitychange", onVisibility),
571
+ () => window.removeEventListener("focus", onFocus),
572
+ () => window.removeEventListener("blur", onBlur)
573
+ );
574
+ }
575
+ getState() {
576
+ return this._state$.getValue();
577
+ }
578
+ state$() {
579
+ return this._state$.asObservable();
580
+ }
581
+ isActive() {
582
+ return this.getState() === "active";
583
+ }
584
+ /** Call fn each time the app comes to the foreground (background → active). */
585
+ onForeground(fn) {
586
+ let prev = this.getState();
587
+ return this._state$.subscribe((s) => {
588
+ if (s === "active" && prev !== "active") fn();
589
+ prev = s;
590
+ });
591
+ }
592
+ /** Call fn each time the app goes to the background (active → background). */
593
+ onBackground(fn) {
594
+ let prev = this.getState();
595
+ return this._state$.subscribe((s) => {
596
+ if (s === "background" && prev !== "background") fn();
597
+ prev = s;
598
+ });
599
+ }
600
+ destroy() {
601
+ for (const fn of this._cleanup) fn();
602
+ this._cleanup = [];
603
+ }
604
+ };
605
+
606
+ // src/device/network-monitor.ts
607
+ var JoopNetworkMonitor = class {
608
+ _adapter;
609
+ _state$;
610
+ _cleanup = [];
611
+ constructor(config = {}) {
612
+ this._adapter = config.adapter ?? JoopPlatform.getAdapter().network ?? null;
613
+ this._state$ = new JoopBehaviorSubject(this._read());
614
+ this._init();
615
+ }
616
+ _init() {
617
+ if (this._adapter) {
618
+ this._state$.next(this._adapter.getState());
619
+ const unsub = this._adapter.onChange((s) => this._state$.next(s));
620
+ this._cleanup.push(unsub);
621
+ return;
622
+ }
623
+ if (typeof window === "undefined") return;
624
+ const refresh = () => this._state$.next(this._read());
625
+ window.addEventListener("online", refresh);
626
+ window.addEventListener("offline", refresh);
627
+ const conn = navigator.connection;
628
+ if (conn) {
629
+ conn.addEventListener("change", refresh);
630
+ this._cleanup.push(() => conn.removeEventListener("change", refresh));
631
+ }
632
+ this._cleanup.push(
633
+ () => window.removeEventListener("online", refresh),
634
+ () => window.removeEventListener("offline", refresh)
635
+ );
636
+ }
637
+ _read() {
638
+ if (this._adapter) return this._adapter.getState();
639
+ if (typeof navigator === "undefined") return { online: false, type: "unknown" };
640
+ const online = navigator.onLine;
641
+ if (!online) return { online: false, type: "offline" };
642
+ const conn = navigator.connection;
643
+ const type = conn ? _mapType(conn.type) : "unknown";
644
+ return {
645
+ online,
646
+ type,
647
+ effectiveType: conn?.effectiveType,
648
+ downlink: conn?.downlink,
649
+ rtt: conn?.rtt,
650
+ saveData: conn?.saveData,
651
+ metered: conn?.metered ?? type === "cellular"
652
+ };
653
+ }
654
+ getState() {
655
+ return this._state$.getValue();
656
+ }
657
+ state$() {
658
+ return this._state$.asObservable();
659
+ }
660
+ isOnline() {
661
+ return this.getState().online;
662
+ }
663
+ isMetered() {
664
+ return this.getState().metered ?? this.getState().type === "cellular";
665
+ }
666
+ /** Call fn when connectivity is regained. */
667
+ onOnline(fn) {
668
+ let prev = this.getState().online;
669
+ return this._state$.subscribe((s) => {
670
+ if (s.online && !prev) fn();
671
+ prev = s.online;
672
+ });
673
+ }
674
+ /** Call fn when connectivity is lost. */
675
+ onOffline(fn) {
676
+ let prev = this.getState().online;
677
+ return this._state$.subscribe((s) => {
678
+ if (!s.online && prev) fn();
679
+ prev = s.online;
680
+ });
681
+ }
682
+ /** Call fn when the connection type changes (e.g. wifi → cellular). */
683
+ onTypeChange(fn) {
684
+ let prev = this.getState().type;
685
+ return this._state$.subscribe((s) => {
686
+ if (s.type !== prev) {
687
+ fn(s.type);
688
+ prev = s.type;
689
+ }
690
+ });
691
+ }
692
+ destroy() {
693
+ for (const fn of this._cleanup) fn();
694
+ this._cleanup = [];
695
+ }
696
+ };
697
+ function _mapType(raw) {
698
+ if (!raw) return "unknown";
699
+ if (raw === "wifi" || raw === "wimax") return "wifi";
700
+ if (raw === "cellular" || raw === "2g" || raw === "3g" || raw === "4g" || raw === "5g") return "cellular";
701
+ if (raw === "ethernet") return "ethernet";
702
+ if (raw === "none" || raw === "offline") return "offline";
703
+ return "unknown";
704
+ }
705
+ var EXPIRY_RE = /\b(0[1-9]|1[0-2])\s*[\/\-]\s*(\d{2}|\d{4})\b/g;
706
+ var HOLDER_RE = /\b([A-Z]{2,}\s+[A-Z]{2,}(?:\s+[A-Z]{2,})?)\b/g;
707
+ function parseCardText(text) {
708
+ const result = {};
709
+ const numMatch = text.replace(/\s/g, "").match(/\d{13,19}/);
710
+ if (numMatch) result.number = numMatch[0];
711
+ const expMatch = EXPIRY_RE.exec(text);
712
+ if (expMatch) result.expiry = expMatch[1] + "/" + expMatch[2].slice(-2);
713
+ const holderMatch = HOLDER_RE.exec(text.toUpperCase());
714
+ if (holderMatch) result.holder = holderMatch[0];
715
+ if (result.number) {
716
+ const n = result.number[0];
717
+ if (n === "4") result.network = "Visa";
718
+ else if (n === "5") result.network = "Mastercard";
719
+ else if (n === "3") result.network = result.number[1] === "4" || result.number[1] === "7" ? "Amex" : "Diners";
720
+ else if (n === "6") result.network = "Discover";
721
+ }
722
+ return result;
723
+ }
724
+ var JoopCardScanner = class {
725
+ _stream = null;
726
+ _video = null;
727
+ _canvas = null;
728
+ _ctx = null;
729
+ _frameInterval = null;
730
+ _frameCount = 0;
731
+ _onResult = null;
732
+ _onError = null;
733
+ _config;
734
+ constructor(config = {}) {
735
+ this._config = {
736
+ frameRate: config.frameRate ?? 5,
737
+ maxFrames: config.maxFrames ?? 30,
738
+ facingMode: config.facingMode ?? "environment"
739
+ };
740
+ }
741
+ async start(container) {
742
+ if (!navigator.mediaDevices?.getUserMedia) {
743
+ throw new Error("Camera not available");
744
+ }
745
+ this._stream = await navigator.mediaDevices.getUserMedia({
746
+ video: { facingMode: this._config.facingMode, width: { ideal: 1280 }, height: { ideal: 720 } }
747
+ });
748
+ this._video = document.createElement("video");
749
+ this._video.srcObject = this._stream;
750
+ this._video.setAttribute("playsinline", "true");
751
+ this._video.setAttribute("autoplay", "true");
752
+ this._video.muted = true;
753
+ if (container) {
754
+ Object.assign(this._video.style, { width: "100%", height: "100%", objectFit: "cover" });
755
+ container.appendChild(this._video);
756
+ }
757
+ await new Promise((resolve) => {
758
+ this._video.onloadedmetadata = () => {
759
+ this._video.play();
760
+ resolve();
761
+ };
762
+ });
763
+ this._canvas = document.createElement("canvas");
764
+ this._ctx = this._canvas.getContext("2d");
765
+ this._startCapture();
766
+ }
767
+ stop() {
768
+ if (this._frameInterval) {
769
+ clearInterval(this._frameInterval);
770
+ this._frameInterval = null;
771
+ }
772
+ if (this._stream) {
773
+ this._stream.getTracks().forEach((t) => t.stop());
774
+ this._stream = null;
775
+ }
776
+ if (this._video) {
777
+ this._video.remove();
778
+ this._video = null;
779
+ }
780
+ }
781
+ onResult(fn) {
782
+ this._onResult = fn;
783
+ return this;
784
+ }
785
+ onError(fn) {
786
+ this._onError = fn;
787
+ return this;
788
+ }
789
+ static fillCard(form, result) {
790
+ if (result.number && form.number) form.number.value = result.number;
791
+ if (result.expiry && form.expiry) form.expiry.value = result.expiry;
792
+ if (result.holder && form.holder) form.holder.value = result.holder;
793
+ }
794
+ _startCapture() {
795
+ const interval = Math.round(1e3 / this._config.frameRate);
796
+ this._frameInterval = setInterval(() => {
797
+ if (!this._video || !this._canvas || !this._ctx) return;
798
+ if (this._frameCount >= this._config.maxFrames) {
799
+ this.stop();
800
+ return;
801
+ }
802
+ this._frameCount++;
803
+ this._canvas.width = this._video.videoWidth;
804
+ this._canvas.height = this._video.videoHeight;
805
+ this._ctx.drawImage(this._video, 0, 0);
806
+ this._processFrame();
807
+ }, interval);
808
+ }
809
+ _processFrame() {
810
+ if (!this._canvas) return;
811
+ try {
812
+ const dataUrl = this._canvas.toDataURL("image/jpeg", 0.7);
813
+ const simulatedText = this._extractTextFromDataUrl(dataUrl);
814
+ if (simulatedText) {
815
+ const result = parseCardText(simulatedText);
816
+ if (result.number) {
817
+ this.stop();
818
+ this._onResult?.(result);
819
+ }
820
+ }
821
+ } catch (err) {
822
+ this._onError?.(err instanceof Error ? err : new Error(String(err)));
823
+ }
824
+ }
825
+ _extractTextFromDataUrl(_dataUrl) {
826
+ return "";
827
+ }
828
+ };
829
+
830
+ // src/device/client-info.service.ts
831
+ function _parseBrowser(ua) {
832
+ if (/Edg\//.test(ua)) return { name: "Edge", version: ua.match(/Edg\/([\d.]+)/)?.[1] ?? "", engine: "Blink" };
833
+ if (/OPR\//.test(ua)) return { name: "Opera", version: ua.match(/OPR\/([\d.]+)/)?.[1] ?? "", engine: "Blink" };
834
+ if (/Chrome\//.test(ua)) return { name: "Chrome", version: ua.match(/Chrome\/([\d.]+)/)?.[1] ?? "", engine: "Blink" };
835
+ if (/Firefox\//.test(ua)) return { name: "Firefox", version: ua.match(/Firefox\/([\d.]+)/)?.[1] ?? "", engine: "Gecko" };
836
+ if (/Safari\//.test(ua) && !/Chrome/.test(ua))
837
+ return { name: "Safari", version: ua.match(/Version\/([\d.]+)/)?.[1] ?? "", engine: "WebKit" };
838
+ if (/MSIE|Trident/.test(ua)) return { name: "IE", version: ua.match(/(?:MSIE |rv:)([\d.]+)/)?.[1] ?? "", engine: "Trident" };
839
+ return { name: "Unknown", version: "", engine: "Unknown" };
840
+ }
841
+ function _parseOs(ua) {
842
+ if (/Windows NT 10/.test(ua)) return { name: "Windows", version: "10/11", arch: ua.includes("x64") ? "x64" : "x86" };
843
+ if (/Windows NT 6\.3/.test(ua)) return { name: "Windows", version: "8.1", arch: "x86" };
844
+ if (/Windows NT 6\.1/.test(ua)) return { name: "Windows", version: "7", arch: "x86" };
845
+ if (/Mac OS X ([\d_]+)/.test(ua)) return { name: "macOS", version: (ua.match(/Mac OS X ([\d_]+)/)?.[1] ?? "").replace(/_/g, "."), arch: "x64" };
846
+ if (/Android ([\d.]+)/.test(ua)) return { name: "Android", version: ua.match(/Android ([\d.]+)/)?.[1] ?? "", arch: "arm" };
847
+ if (/iPhone OS ([\d_]+)/.test(ua)) return { name: "iOS", version: (ua.match(/iPhone OS ([\d_]+)/)?.[1] ?? "").replace(/_/g, "."), arch: "arm" };
848
+ if (/iPad.*OS ([\d_]+)/.test(ua)) return { name: "iPadOS", version: (ua.match(/OS ([\d_]+)/)?.[1] ?? "").replace(/_/g, "."), arch: "arm" };
849
+ if (/Linux/.test(ua)) return { name: "Linux", version: "", arch: ua.includes("x86_64") ? "x64" : "x86" };
850
+ return { name: "Unknown", version: "", arch: "Unknown" };
851
+ }
852
+ function _detectDevice(ua) {
853
+ if (/tablet|ipad|playbook|silk/i.test(ua)) return "tablet";
854
+ if (/mobile|android|iphone|ipod|blackberry|windows phone/i.test(ua)) return "mobile";
855
+ return "desktop";
856
+ }
857
+ function _getNetwork() {
858
+ const conn = navigator.connection ?? navigator.mozConnection ?? navigator.webkitConnection;
859
+ return {
860
+ online: navigator.onLine,
861
+ type: conn?.type ?? "unknown",
862
+ effectiveType: conn?.effectiveType ?? "unknown",
863
+ downlink: conn?.downlink ?? null
864
+ };
865
+ }
866
+ var JoopClientInfoService = class {
867
+ _config;
868
+ constructor(config = {}) {
869
+ this._config = {
870
+ includeLocation: config.includeLocation ?? false,
871
+ ipApiUrl: config.ipApiUrl ?? "https://api.ipify.org?format=json",
872
+ timeout: config.timeout ?? 5e3
873
+ };
874
+ }
875
+ async collect() {
876
+ const ua = navigator.userAgent;
877
+ const [ip, location2] = await Promise.all([
878
+ this._fetchIp(),
879
+ this._config.includeLocation ? this._fetchLocation() : Promise.resolve(null)
880
+ ]);
881
+ return {
882
+ ip,
883
+ userAgent: ua,
884
+ browser: _parseBrowser(ua),
885
+ os: _parseOs(ua),
886
+ device: _detectDevice(ua),
887
+ screen: {
888
+ width: screen.width,
889
+ height: screen.height,
890
+ colorDepth: screen.colorDepth,
891
+ pixelRatio: devicePixelRatio
892
+ },
893
+ network: _getNetwork(),
894
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
895
+ locale: navigator.language,
896
+ location: location2,
897
+ timestamp: Date.now()
898
+ };
899
+ }
900
+ /** Collect and return as a flat record safe for HTTP headers or analytics events */
901
+ async collectFlat() {
902
+ const info = await this.collect();
903
+ return {
904
+ ip: info.ip,
905
+ userAgent: info.userAgent,
906
+ browser: `${info.browser.name} ${info.browser.version}`,
907
+ browserEngine: info.browser.engine,
908
+ os: `${info.os.name} ${info.os.version}`,
909
+ osArch: info.os.arch,
910
+ device: info.device,
911
+ screenWidth: info.screen.width,
912
+ screenHeight: info.screen.height,
913
+ pixelRatio: info.screen.pixelRatio,
914
+ online: info.network.online,
915
+ networkType: info.network.type,
916
+ effectiveType: info.network.effectiveType,
917
+ timezone: info.timezone,
918
+ locale: info.locale,
919
+ lat: info.location?.latitude ?? null,
920
+ lng: info.location?.longitude ?? null,
921
+ timestamp: info.timestamp
922
+ };
923
+ }
924
+ async _fetchIp() {
925
+ try {
926
+ const controller = new AbortController();
927
+ const tid = setTimeout(() => controller.abort(), this._config.timeout);
928
+ const res = await fetch(this._config.ipApiUrl, { signal: controller.signal });
929
+ clearTimeout(tid);
930
+ const data = await res.json();
931
+ return data.ip ?? data.query ?? null;
932
+ } catch {
933
+ return null;
934
+ }
935
+ }
936
+ _fetchLocation() {
937
+ return new Promise((resolve) => {
938
+ if (!navigator.geolocation) {
939
+ resolve(null);
940
+ return;
941
+ }
942
+ navigator.geolocation.getCurrentPosition(
943
+ (pos) => resolve({ latitude: pos.coords.latitude, longitude: pos.coords.longitude, accuracy: pos.coords.accuracy }),
944
+ () => resolve(null),
945
+ { timeout: this._config.timeout, maximumAge: 6e4 }
946
+ );
947
+ });
948
+ }
949
+ };
950
+
951
+ exports.JoopAppLifecycle = JoopAppLifecycle;
952
+ exports.JoopBiometricAuth = JoopBiometricAuth;
953
+ exports.JoopCardScanner = JoopCardScanner;
954
+ exports.JoopClientInfoService = JoopClientInfoService;
955
+ exports.JoopDeviceFingerprintService = JoopDeviceFingerprintService;
956
+ exports.JoopKeyVault = JoopKeyVault;
957
+ exports.JoopNetworkMonitor = JoopNetworkMonitor;
958
+ exports.JoopPushService = JoopPushService;
959
+ //# sourceMappingURL=index.js.map
960
+ //# sourceMappingURL=index.js.map